summaryrefslogtreecommitdiff
path: root/storage/ndb/tools
diff options
context:
space:
mode:
Diffstat (limited to 'storage/ndb/tools')
-rw-r--r--storage/ndb/tools/Makefile.am156
-rwxr-xr-xstorage/ndb/tools/clean-links.sh21
-rw-r--r--storage/ndb/tools/delete_all.cpp193
-rw-r--r--storage/ndb/tools/desc.cpp119
-rw-r--r--storage/ndb/tools/drop_index.cpp95
-rw-r--r--storage/ndb/tools/drop_tab.cpp96
-rw-r--r--storage/ndb/tools/listTables.cpp232
-rw-r--r--storage/ndb/tools/make-errors.pl181
-rwxr-xr-xstorage/ndb/tools/make-links.sh20
-rw-r--r--storage/ndb/tools/ndb_test_platform.cpp95
-rw-r--r--storage/ndb/tools/ndbsql.cpp957
-rw-r--r--storage/ndb/tools/old_dirs/copy_tab/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/cpcc/Makefile12
-rw-r--r--storage/ndb/tools/old_dirs/create_index/Makefile11
-rw-r--r--storage/ndb/tools/old_dirs/delete_all/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/desc/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/drop_index/Makefile11
-rw-r--r--storage/ndb/tools/old_dirs/drop_tab/Makefile11
-rw-r--r--storage/ndb/tools/old_dirs/list_tables/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/Makefile.PL158
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net.pm42
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Base.pm12
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Client.pm252
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Command.pm641
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Config.pm235
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Database.pm321
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Env.pm94
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Node.pm747
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeApi.pm84
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeDb.pm116
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeMgmt.pm318
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Server.pm149
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerINET.pm116
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerUNIX.pm54
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run.pm40
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Base.pm12
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Database.pm89
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Env.pm84
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Node.pm114
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util.pm37
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Base.pm113
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Dir.pm170
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Event.pm103
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/File.pm163
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/IO.pm213
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Lock.pm136
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Log.pm367
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Socket.pm158
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketINET.pm86
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketUNIX.pm76
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/ndbnet.pl339
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/ndbnetd.pl400
-rw-r--r--storage/ndb/tools/old_dirs/ndbnet/ndbrun33
-rw-r--r--storage/ndb/tools/old_dirs/ndbsql/Makefile44
-rw-r--r--storage/ndb/tools/old_dirs/select_all/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/select_count/Makefile9
-rw-r--r--storage/ndb/tools/old_dirs/src/counterviewer/CounterViewer.java725
-rw-r--r--storage/ndb/tools/restore/Restore.cpp947
-rw-r--r--storage/ndb/tools/restore/Restore.hpp379
-rw-r--r--storage/ndb/tools/restore/consumer.cpp115
-rw-r--r--storage/ndb/tools/restore/consumer.hpp36
-rw-r--r--storage/ndb/tools/restore/consumer_printer.cpp55
-rw-r--r--storage/ndb/tools/restore/consumer_printer.hpp50
-rw-r--r--storage/ndb/tools/restore/consumer_restore.cpp674
-rw-r--r--storage/ndb/tools/restore/consumer_restore.hpp93
-rw-r--r--storage/ndb/tools/restore/consumer_restorem.cpp653
-rw-r--r--storage/ndb/tools/restore/restore_main.cpp411
-rwxr-xr-xstorage/ndb/tools/rgrep194
-rw-r--r--storage/ndb/tools/select_all.cpp358
-rw-r--r--storage/ndb/tools/select_count.cpp212
-rw-r--r--storage/ndb/tools/waiter.cpp314
71 files changed, 13596 insertions, 0 deletions
diff --git a/storage/ndb/tools/Makefile.am b/storage/ndb/tools/Makefile.am
new file mode 100644
index 00000000000..c350fb0a141
--- /dev/null
+++ b/storage/ndb/tools/Makefile.am
@@ -0,0 +1,156 @@
+
+ndbtools_PROGRAMS = \
+ ndb_test_platform \
+ ndb_waiter \
+ ndb_drop_table \
+ ndb_delete_all \
+ ndb_desc \
+ ndb_drop_index \
+ ndb_show_tables \
+ ndb_select_all \
+ ndb_select_count \
+ ndb_restore
+
+tools_common_sources = ../test/src/NDBT_ReturnCodes.cpp \
+ ../test/src/NDBT_Table.cpp \
+ ../test/src/NDBT_Output.cpp
+
+ndb_test_platform_SOURCES = ndb_test_platform.cpp
+ndb_waiter_SOURCES = waiter.cpp $(tools_common_sources)
+ndb_delete_all_SOURCES = delete_all.cpp $(tools_common_sources)
+ndb_desc_SOURCES = desc.cpp $(tools_common_sources)
+ndb_drop_index_SOURCES = drop_index.cpp $(tools_common_sources)
+ndb_drop_table_SOURCES = drop_tab.cpp $(tools_common_sources)
+ndb_show_tables_SOURCES = listTables.cpp $(tools_common_sources)
+ndb_select_all_SOURCES = select_all.cpp \
+ ../test/src/NDBT_ResultRow.cpp \
+ $(tools_common_sources)
+ndb_select_count_SOURCES = select_count.cpp $(tools_common_sources)
+ndb_restore_SOURCES = restore/restore_main.cpp \
+ restore/consumer.cpp \
+ restore/consumer_restore.cpp \
+ restore/consumer_printer.cpp \
+ restore/Restore.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitools.mk.am
+
+ndb_test_platform_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_waiter_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_drop_table_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_delete_all_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_desc_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_drop_index_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_show_tables_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_select_all_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_select_count_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_restore_LDFLAGS = @ndb_bin_am_ldflags@
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: \
+ ndb_waiter.dsp \
+ ndb_drop_table.dsp \
+ ndb_delete_all.dsp \
+ ndb_desc.dsp \
+ ndb_drop_index.dsp \
+ ndb_show_tables.dsp \
+ ndb_select_all.dsp \
+ ndb_select_count.dsp
+
+ndb_waiter.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_waiter
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_waiter_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_drop_table.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_drop_table
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_drop_table_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_delete_all.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_delete_all
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_delete_all_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_desc.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_desc
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_desc_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_drop_index.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_drop_index
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_drop_index_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_show_tables.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_show_tables
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_show_tables_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_select_all.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_select_all
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_select_all_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+ndb_select_count.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ ndb_select_count
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_select_count_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
diff --git a/storage/ndb/tools/clean-links.sh b/storage/ndb/tools/clean-links.sh
new file mode 100755
index 00000000000..01820f30616
--- /dev/null
+++ b/storage/ndb/tools/clean-links.sh
@@ -0,0 +1,21 @@
+#! /bin/sh
+
+# 1 - Dir
+# 2 - Link dst
+
+if [ $# -lt 1 ]
+then
+ exit 0
+fi
+
+files=`find $1 -type l -maxdepth 1`
+res=$?
+if [ $res -ne 0 ] || [ "$files" = "" ]
+then
+ exit 0
+fi
+
+rm -f $files
+
+
+
diff --git a/storage/ndb/tools/delete_all.cpp b/storage/ndb/tools/delete_all.cpp
new file mode 100644
index 00000000000..2c395a67900
--- /dev/null
+++ b/storage/ndb/tools/delete_all.cpp
@@ -0,0 +1,193 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+
+static int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism=240);
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname\n"\
+ "This program will delete all records in the specified table using scan delete.\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_delete_all.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname );
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Check if table exists in db
+ int res = NDBT_OK;
+ for(int i = 0; i<argc; i++){
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, argv[i]);
+ if(pTab == NULL){
+ ndbout << " Table " << argv[i] << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ ndbout << "Deleting all from " << argv[i] << "...";
+ if(clear_table(&MyNdb, pTab) == NDBT_FAILED){
+ res = NDBT_FAILED;
+ ndbout << "FAILED" << endl;
+ }
+ }
+ return NDBT_ProgramExit(res);
+}
+
+
+int clear_table(Ndb* pNdb, const NdbDictionary::Table* pTab, int parallelism)
+{
+ // Scan all records exclusive and delete
+ // them one by one
+ int retryAttempt = 0;
+ const int retryMax = 10;
+ int deletedRows = 0;
+ int check;
+ NdbTransaction *pTrans;
+ NdbScanOperation *pOp;
+ NdbError err;
+
+ int par = parallelism;
+ while (true){
+ restart:
+ if (retryAttempt++ >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ err = pNdb->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ goto failed;
+ }
+
+ pOp = pTrans->getNdbScanOperation(pTab->getName());
+ if (pOp == NULL) {
+ goto failed;
+ }
+
+ if( pOp->readTuples(NdbOperation::LM_Exclusive,par) ) {
+ goto failed;
+ }
+
+ if(pTrans->execute(NdbTransaction::NoCommit) != 0){
+ err = pTrans->getNdbError();
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ goto failed;
+ }
+
+ while((check = pOp->nextResult(true)) == 0){
+ do {
+ if (pOp->deleteCurrentTuple() != 0){
+ goto failed;
+ }
+ deletedRows++;
+ } while((check = pOp->nextResult(false)) == 0);
+
+ if(check != -1){
+ check = pTrans->execute(NdbTransaction::Commit);
+ pTrans->restart();
+ }
+
+ err = pTrans->getNdbError();
+ if(check == -1){
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ par = 1;
+ goto restart;
+ }
+ goto failed;
+ }
+ }
+ if(check == -1){
+ err = pTrans->getNdbError();
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ par = 1;
+ goto restart;
+ }
+ goto failed;
+ }
+ pNdb->closeTransaction(pTrans);
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+
+ failed:
+ if(pTrans != 0) pNdb->closeTransaction(pTrans);
+ ERR(err);
+ return (err.code != 0 ? err.code : NDBT_FAILED);
+}
diff --git a/storage/ndb/tools/desc.cpp b/storage/ndb/tools/desc.cpp
new file mode 100644
index 00000000000..be0f6942db5
--- /dev/null
+++ b/storage/ndb/tools/desc.cpp
@@ -0,0 +1,119 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+#include <NDBT.hpp>
+#include <NdbApi.hpp>
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static int _unqualified = 0;
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "unqualified", 'u', "Use unqualified table names",
+ (gptr*) &_unqualified, (gptr*) &_unqualified, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname\n"\
+ "This program list all properties of table(s) in NDB Cluster.\n"\
+ " ex: desc T1 T2 T4\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_desc.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname);
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ const NdbDictionary::Dictionary * dict= MyNdb.getDictionary();
+ for (int i = 0; i < argc; i++) {
+ NDBT_Table* pTab = (NDBT_Table*)dict->getTable(argv[i]);
+ if (pTab != 0){
+ ndbout << (* pTab) << endl;
+
+ NdbDictionary::Dictionary::List list;
+ if (dict->listIndexes(list, argv[i]) != 0){
+ ndbout << argv[i] << ": " << dict->getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ ndbout << "-- Indexes -- " << endl;
+ ndbout << "PRIMARY KEY(";
+ unsigned j;
+ for (j= 0; (int)j < pTab->getNoOfPrimaryKeys(); j++)
+ {
+ const NdbDictionary::Column * col = pTab->getColumn(pTab->getPrimaryKey(j));
+ ndbout << col->getName();
+ if ((int)j < pTab->getNoOfPrimaryKeys()-1)
+ ndbout << ", ";
+ }
+ ndbout << ") - UniqueHashIndex" << endl;
+
+ for (j= 0; j < list.count; j++) {
+ NdbDictionary::Dictionary::List::Element& elt = list.elements[j];
+ const NdbDictionary::Index *pIdx = dict->getIndex(elt.name, argv[i]);
+ if (!pIdx){
+ ndbout << argv[i] << ": " << dict->getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ ndbout << (*pIdx) << endl;
+ }
+ ndbout << endl;
+ }
+ else
+ ndbout << argv[i] << ": " << dict->getNdbError() << endl;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/tools/drop_index.cpp b/storage/ndb/tools/drop_index.cpp
new file mode 100644
index 00000000000..e2bf7f0bfae
--- /dev/null
+++ b/storage/ndb/tools/drop_index.cpp
@@ -0,0 +1,95 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "<indexname>+\n"\
+ "This program will drop index(es) in Ndb\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ "d:t:O,/tmp/ndb_drop_index.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ if (argc < 1) {
+ usage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,3) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname );
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ int res = 0;
+ for(int i = 0; i<argc; i++){
+ ndbout << "Dropping index " << argv[i] << "...";
+ int tmp;
+ if((tmp = MyNdb.getDictionary()->dropIndex(argv[i], 0)) != 0){
+ ndbout << endl << MyNdb.getDictionary()->getNdbError() << endl;
+ res = tmp;
+ } else {
+ ndbout << "OK" << endl;
+ }
+ }
+
+ if(res != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/tools/drop_tab.cpp b/storage/ndb/tools/drop_tab.cpp
new file mode 100644
index 00000000000..991e1505486
--- /dev/null
+++ b/storage/ndb/tools/drop_tab.cpp
@@ -0,0 +1,96 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname\n"\
+ "This program will drop one table in Ndb\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ "d:t:O,/tmp/ndb_drop_table.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ if (argc < 1) {
+ usage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,3) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname );
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ int res = 0;
+ for(int i = 0; i<argc; i++){
+ ndbout << "Dropping table " << argv[i] << "...";
+ int tmp;
+ if((tmp = MyNdb.getDictionary()->dropTable(argv[i])) != 0){
+ ndbout << endl << MyNdb.getDictionary()->getNdbError() << endl;
+ res = tmp;
+ } else {
+ ndbout << "OK" << endl;
+ }
+ }
+
+ if(res != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/tools/listTables.cpp b/storage/ndb/tools/listTables.cpp
new file mode 100644
index 00000000000..b4a2235f73b
--- /dev/null
+++ b/storage/ndb/tools/listTables.cpp
@@ -0,0 +1,232 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * list_tables
+ *
+ * List objects(tables, triggers, etc.) in NDB Cluster
+ *
+ */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+static Ndb_cluster_connection *ndb_cluster_connection= 0;
+static Ndb* ndb = 0;
+static const NdbDictionary::Dictionary * dic = 0;
+static int _unqualified = 0;
+
+static void
+fatal(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[500];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << buf;
+ if (ndb)
+ ndbout << " - " << ndb->getNdbError();
+ ndbout << endl;
+ NDBT_ProgramExit(NDBT_FAILED);
+ exit(1);
+}
+
+static void
+fatal_dict(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[500];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << buf;
+ if (dic)
+ ndbout << " - " << dic->getNdbError();
+ ndbout << endl;
+ NDBT_ProgramExit(NDBT_FAILED);
+ exit(1);
+}
+
+static void
+list(const char * tabname,
+ NdbDictionary::Object::Type type)
+{
+ NdbDictionary::Dictionary::List list;
+ if (tabname == 0) {
+ if (dic->listObjects(list, type) == -1)
+ fatal_dict("listObjects");
+ } else {
+ if (dic->listIndexes(list, tabname) == -1)
+ fatal_dict("listIndexes");
+ }
+ if (ndb->usingFullyQualifiedNames())
+ ndbout_c("%-5s %-20s %-8s %-7s %-12s %-8s %s", "id", "type", "state", "logging", "database", "schema", "name");
+ else
+ ndbout_c("%-5s %-20s %-8s %-7s %s", "id", "type", "state", "logging", "name");
+ for (unsigned i = 0; i < list.count; i++) {
+ NdbDictionary::Dictionary::List::Element& elt = list.elements[i];
+ char type[100];
+ bool isTable = false;
+ switch (elt.type) {
+ case NdbDictionary::Object::SystemTable:
+ strcpy(type, "SystemTable");
+ isTable = true;
+ break;
+ case NdbDictionary::Object::UserTable:
+ strcpy(type, "UserTable");
+ isTable = true;
+ break;
+ case NdbDictionary::Object::UniqueHashIndex:
+ strcpy(type, "UniqueHashIndex");
+ isTable = true;
+ break;
+ case NdbDictionary::Object::OrderedIndex:
+ strcpy(type, "OrderedIndex");
+ isTable = true;
+ break;
+ case NdbDictionary::Object::HashIndexTrigger:
+ strcpy(type, "HashIndexTrigger");
+ break;
+ case NdbDictionary::Object::IndexTrigger:
+ strcpy(type, "IndexTrigger");
+ break;
+ case NdbDictionary::Object::SubscriptionTrigger:
+ strcpy(type, "SubscriptionTrigger");
+ break;
+ case NdbDictionary::Object::ReadOnlyConstraint:
+ strcpy(type, "ReadOnlyConstraint");
+ break;
+ default:
+ sprintf(type, "%d", (int)elt.type);
+ break;
+ }
+ char state[100];
+ switch (elt.state) {
+ case NdbDictionary::Object::StateOffline:
+ strcpy(state, "Offline");
+ break;
+ case NdbDictionary::Object::StateBuilding:
+ strcpy(state, "Building");
+ break;
+ case NdbDictionary::Object::StateDropping:
+ strcpy(state, "Dropping");
+ break;
+ case NdbDictionary::Object::StateOnline:
+ strcpy(state, "Online");
+ break;
+ case NdbDictionary::Object::StateBroken:
+ strcpy(state, "Broken");
+ break;
+ default:
+ sprintf(state, "%d", (int)elt.state);
+ break;
+ }
+ char store[100];
+ if (! isTable)
+ strcpy(store, "-");
+ else {
+ switch (elt.store) {
+ case NdbDictionary::Object::StoreTemporary:
+ strcpy(store, "No");
+ break;
+ case NdbDictionary::Object::StorePermanent:
+ strcpy(store, "Yes");
+ break;
+ default:
+ sprintf(state, "%d", (int)elt.store);
+ break;
+ }
+ }
+ if (ndb->usingFullyQualifiedNames())
+ ndbout_c("%-5d %-20s %-8s %-7s %-12s %-8s %s", elt.id, type, state, store, (elt.database)?elt.database:"", (elt.schema)?elt.schema:"", elt.name);
+ else
+ ndbout_c("%-5d %-20s %-8s %-7s %s", elt.id, type, state, store, elt.name);
+ }
+}
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static int _loops;
+static int _type;
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_show_tables"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "loops", 'l', "loops",
+ (gptr*) &_loops, (gptr*) &_loops, 0,
+ GET_INT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0 },
+ { "type", 't', "type",
+ (gptr*) &_type, (gptr*) &_type, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "unqualified", 'u', "Use unqualified table names",
+ (gptr*) &_unqualified, (gptr*) &_unqualified, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname\n"\
+ "This program list all system objects in NDB Cluster.\n"\
+ "Type of objects to display can be limited with -t option\n"\
+ " ex: ndb_show_tables -t 2 would show all UserTables\n"\
+ "To show all indexes for a table write table name as final argument\n"\
+ " ex: ndb_show_tables T1\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char* _tabname;
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_show_tables.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ _tabname = argv[0];
+
+ ndb_cluster_connection = new Ndb_cluster_connection(opt_connect_str);
+ if (ndb_cluster_connection->connect(12,5,1))
+ fatal("Unable to connect to management server.");
+ if (ndb_cluster_connection->wait_until_ready(30,0) < 0)
+ fatal("Cluster nodes not ready in 30 seconds.");
+
+ ndb = new Ndb(ndb_cluster_connection, _dbname);
+ if (ndb->init() != 0)
+ fatal("init");
+ dic = ndb->getDictionary();
+ for (int i = 0; _loops == 0 || i < _loops; i++) {
+ list(_tabname, (NdbDictionary::Object::Type)_type);
+ }
+ delete ndb;
+ delete ndb_cluster_connection;
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+// vim: set sw=4:
diff --git a/storage/ndb/tools/make-errors.pl b/storage/ndb/tools/make-errors.pl
new file mode 100644
index 00000000000..65819209a89
--- /dev/null
+++ b/storage/ndb/tools/make-errors.pl
@@ -0,0 +1,181 @@
+#! /usr/local/bin/perl
+
+use strict;
+use Getopt::Long;
+use XML::Parser;
+
+(my $progname = $0) =~ s!^.*/!!;
+
+sub usage {
+ my $errstr = "@_";
+ while (chomp($errstr)) {}
+ print <<END;
+$progname: $errstr
+$progname -- read codes.xml and write codes.hpp and codes.cpp
+usage: $progname [options] codes.xml
+-c check xml file only
+-d check xml file and show diff against old hpp and cpp
+END
+ exit(1);
+}
+
+my $opts = {};
+opts: {
+ local $SIG{__WARN__} = \&usage;
+ GetOptions($opts, qw(c d));
+}
+@ARGV == 1 or usage("one filename argument expected");
+my $filexml = shift;
+$filexml =~ /^(.*)\.xml$/ or usage("$filexml does not end in .xml");
+my $filehpp = "$1.hpp";
+my $filecpp = "$1.cpp";
+
+my $temphpp = "$filehpp-new";
+my $tempcpp = "$filecpp-new";
+unlink $temphpp, $tempcpp;
+open(HPP, ">$temphpp") or die "$temphpp: $!\n";
+open(CPP, ">$tempcpp") or die "$tempcpp: $!\n";
+
+my $i2 = " " x 2;
+my $i4 = " " x 4;
+my $lb = "{";
+my $rb = "}";
+
+sub disclaimer {
+ my $filename = shift;
+ return <<END;
+/*
+ * $filename -- DO NOT EDIT !!
+ *
+ * To create a new version (both *.hpp and *.cpp):
+ *
+ * 1) edit $filexml
+ * 2) perl tools/$progname $filexml
+ * 3) check all files (*.xml *.hpp *.cpp) into CVS
+ *
+ * On RedHat linux requires perl-XML-Parser package.
+ */
+END
+}
+
+my $classname = $filehpp;
+$classname =~ s!^.*/!!;
+$classname =~ s/\.hpp$//;
+
+sub handle_init {
+ my($parser) = @_;
+ my $guard = $filehpp;
+ $guard =~ s!^.*/!!;
+ $guard =~ s!([a-z])([A-Z])!${1}_${2}!g;
+ $guard =~ s!\.!_!g;
+ $guard = uc($guard);
+ print HPP "#ifndef $guard\n#define $guard\n\n";
+ print HPP disclaimer($filehpp), "\n";
+ print HPP "class $classname $lb\n";
+ print HPP "${i2}enum Value $lb\n";
+ print CPP disclaimer($filecpp), "\n";
+ print CPP "/* included in Ndberror.cpp */\n\n";
+}
+
+my %classhash = (
+ ApplicationError => 1,
+ NoDataFound => 1,
+ ConstraintViolation => 1,
+ SchemaError => 1,
+ UserDefinedError => 1,
+ InsufficientSpace => 1,
+ TemporaryResourceError => 1,
+ NodeRecoveryError => 1,
+ OverloadError => 1,
+ TimeoutExpired => 1,
+ UnknownResultError => 1,
+ InternalError => 1,
+ FunctionNotImplemented => 1,
+ UnknownErrorCode => 1,
+ NodeShutdown => 1,
+);
+
+my $section = undef;
+my %codehash = ();
+my %namehash = ();
+
+sub handle_start {
+ my($parser, $tag, %attr) = @_;
+ if ($tag eq 'Error') {
+ return;
+ }
+ if ($tag eq 'Section') {
+ $section = $attr{name};
+ $section =~ /^\w+$/ or
+ $parser->xpcroak("invalid or missing section name");
+ return;
+ }
+ if ($tag eq 'Code') {
+ print HPP ",\n" if %codehash;
+ print CPP ",\n" if %codehash;
+ my $name = $attr{name};
+ my $class = $attr{class};
+ my $code = $attr{code};
+ my $message = $attr{message};
+ $name =~ /^\w+$/ or
+ $parser->xpcroak("invalid or missing error name '$name'");
+ $namehash{$name}++ and
+ $parser->xpcroak("duplicate error name '$name'");
+ $classhash{$class} or
+ $parser->xpcroak("invalid or missing error class '$class'");
+ $code =~ /^\d+$/ or
+ $parser->xpcroak("invalid or missing error code '$code'");
+ $codehash{$code}++ and
+ $parser->xpcroak("duplicate error code '$code'");
+ $message =~ /\S/ or
+ $parser->xpcroak("invalid or missing error message '$message'");
+ $message =~ s/^\s+|\s+$//g;
+ my $enum = "${section}_${name}";
+ print HPP "${i4}$enum = $code";
+ print CPP "${i2}$lb ${classname}::$enum,\n";
+ print CPP "${i4}NdbError::$class,\n";
+ print CPP "${i4}\"$message\"\n";
+ print CPP "${i2}$rb";
+ return;
+ }
+ $parser->xpcroak("unknown tag $tag");
+}
+
+sub handle_end {
+ my($parser, $tag) = @_;
+}
+
+sub handle_final {
+ print HPP "\n" if %codehash;
+ print HPP "${i2}$rb;\n";
+ print HPP "$rb;\n\n#endif\n";
+ print CPP ",\n" if 1;
+ return 1;
+}
+
+my $parser = new XML::Parser(
+ ParseParamEnt => 1,
+ Handlers => {
+ Init => \&handle_init,
+ Start => \&handle_start,
+ End => \&handle_end,
+ Final => \&handle_final,
+ },
+ ErrorContext => 0,
+);
+eval {
+ $parser->parsefile($filexml);
+};
+if ($@) {
+ my $errstr = join("\n", grep(m!\S! && ! m!^\s*at\s!, split(/\n/, $@)));
+ die "$filexml:\n$errstr\n";
+}
+
+close(HPP);
+close(CPP);
+rename($temphpp, $filehpp);
+rename($tempcpp, $filecpp);
+
+1;
+
+# vim:set sw=4:
diff --git a/storage/ndb/tools/make-links.sh b/storage/ndb/tools/make-links.sh
new file mode 100755
index 00000000000..e0c4f55986e
--- /dev/null
+++ b/storage/ndb/tools/make-links.sh
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+# 1 - Link top src
+# 2 - Link dst
+
+if [ $# -lt 2 ]
+then
+ exit 0
+fi
+
+name=`basename $2`
+files=`find $1/$name -type f -name '*.h*'`
+
+for i in $files
+do
+ ln -s $i $2/`basename $i`
+done
+
+
+
diff --git a/storage/ndb/tools/ndb_test_platform.cpp b/storage/ndb/tools/ndb_test_platform.cpp
new file mode 100644
index 00000000000..88f21b31d58
--- /dev/null
+++ b/storage/ndb/tools/ndb_test_platform.cpp
@@ -0,0 +1,95 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include <ndb_global.h>
+#include <my_sys.h>
+#include <BaseString.hpp>
+
+/*
+ * Test BaseString::snprintf
+ */
+
+static
+int test_snprintf(const char * fmt, int buf_sz, int result)
+{
+ int ret;
+ char buf[100];
+ ret = BaseString::snprintf(buf, buf_sz, fmt);
+
+ if(ret < 0)
+ {
+ printf("BaseString::snprint returns %d with size=%d and strlen(fmt)=%d\n",
+ ret, buf_sz, (int) strlen(fmt));
+ return -1;
+ }
+
+ if(ret+1 == buf_sz)
+ {
+ printf("BaseString::snprint truncates returns %d with size=%d and strlen(fmt)=%d\n",
+ ret, buf_sz, (int) strlen(fmt));
+ return -1;
+ }
+
+ if(ret != result)
+ {
+ printf("BaseString::snprint returns incorrect value: returned=%d != expected=%d\n",
+ ret, result);
+ return -1;
+ }
+
+ for(ret = 0; ret+1 < buf_sz && ret < result; ret++)
+ {
+ if(buf[ret] != fmt[ret])
+ {
+ printf("BaseString::snprint Incorrect value in output buffer: "
+ "size=%d returned=expected=%d at pos=%d result=%d != expected=%d\n",
+ buf_sz, result, ret, buf[ret], fmt[ret]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+main(void)
+{
+ /*
+ * Test BaseString::snprintf
+ */
+
+ if(test_snprintf("test", 1, 4))
+ return -1;
+
+ if(test_snprintf("test", 0, 4))
+ return -1;
+
+ if(test_snprintf("test", 100, 4))
+ return -1;
+
+ /*
+ * Test UintPtr
+ */
+
+ if (sizeof(UintPtr) != sizeof(Uint32*))
+ {
+ printf("sizeof(UintPtr)=%d != sizeof(Uint32*)=%d\n",
+ (int) sizeof(UintPtr), (int) sizeof(Uint32*));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/storage/ndb/tools/ndbsql.cpp b/storage/ndb/tools/ndbsql.cpp
new file mode 100644
index 00000000000..1997e4abebd
--- /dev/null
+++ b/storage/ndb/tools/ndbsql.cpp
@@ -0,0 +1,957 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*******************************************************************************
+ * NDB Cluster NDB SQL -- A simple SQL Command-line Interface
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef NDB_MACOSX
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+#include <errno.h>
+#include <editline/editline.h>
+#include <NdbOut.hpp>
+#include <ctype.h>
+#include <wctype.h>
+
+#ifndef SQL_BLOB
+#define SQL_BLOB 30
+#endif
+#ifndef SQL_CLOB
+#define SQL_CLOB 40
+#endif
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Readline and string handling
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+#define MAXBUF 2048
+static char* s_readBuf;
+static int s_bufSize = MAXBUF;
+
+static char*
+readSQL_File(FILE* inputFile)
+{
+ int c;
+ int i = 0;
+ if (feof(inputFile))
+ return 0;
+ while ((c = getc(inputFile)) != EOF) {
+ if (i == s_bufSize-1) {
+ s_bufSize *= 2;
+ s_readBuf = (char*)realloc(s_readBuf, s_bufSize);
+ }
+ s_readBuf[i] = c;
+ if (c == '\n')
+ break;
+ i++;
+ }
+ s_readBuf[i] = 0;
+ return s_readBuf;
+}
+
+static char*
+readline_gets(const char* prompt, bool batchMode, FILE* inputFile)
+{
+ static char *line_read = (char *)NULL;
+
+ // Disable the default file-name completion action of TAB
+ // rl_bind_key ('\t', rl_insert);
+
+ if (batchMode)
+ /* Read one line from a file. */
+ line_read = readSQL_File(inputFile);
+ else
+ /* Get a line from the user. */
+ line_read = readline(prompt);
+
+ /* If the line has any text in it, save it in the history. */
+ if (!batchMode)
+ if (line_read && *line_read) add_history(line_read);
+
+ return (line_read);
+}
+
+#ifdef NDB_WIN32
+extern "C"
+{
+ char* readline(const char* prompt)
+ {
+ fputs(prompt, stdout);
+ return fgets(s_readBuf, MAXBUF, stdin);
+ }
+ void add_history(char*)
+ {
+ }
+}
+#endif
+
+bool emptyString(const char* s) {
+ if (s == NULL) {
+ return true;
+ }
+
+ for (unsigned int i = 0; i < strlen(s); ++i) {
+ if (! isspace(s[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: ODBC Handling
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+
+#include <sqlext.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef NDB_MACOSX
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+/**
+ * In the case where the user types a SELECT statement,
+ * the function fetches and displays all rows of the result set.
+ *
+ * This example illustrates the use of GetDiagField to identify the
+ * type of SQL statement executed and, for SQL statements where the
+ * row count is defined on all implementations, the use of GetDiagField
+ * to obtain the row count.
+ */
+#define MAXCOLS 100
+#undef max
+#define max(a,b) ((a)>(b)?(a):(b))
+
+#define MAX_MESSAGE 500
+
+void getDiag(SQLSMALLINT type, SQLHANDLE handle, unsigned k, unsigned count)
+{
+ char message[MAX_MESSAGE];
+ char state[6];
+ SQLINTEGER native;
+
+ SQLSMALLINT length = -1;
+ memset(message, 0, MAX_MESSAGE);
+ int ret = SQLGetDiagRec(type, handle, k, (SQLCHAR*)state,
+ &native, (SQLCHAR*)message, MAX_MESSAGE, &length);
+ if (ret == SQL_NO_DATA) {
+ ndbout << "No error diagnostics available" << endl;
+ return;
+ }
+ ndbout << message << endl;
+
+ if (k <= count && ret != SQL_SUCCESS)
+ ndbout_c("SQLGetDiagRec %d of %d: return %d != SQL_SUCCESS",
+ k, count, (int)ret);
+ if (k <= count && (SQLSMALLINT) strlen(message) != length)
+ ndbout_c("SQLGetDiagRec %d of %d: message length %d != %d",
+ k, count, strlen(message), length);
+ if (k > count && ret != SQL_NO_DATA)
+ ndbout_c("SQLGetDiagRec %d of %d: return %d != SQL_NO_DATA",
+ k, count, (int)ret);
+}
+
+int print_err(SQLSMALLINT handletype, SQLHDBC hdbc) {
+ getDiag(handletype, hdbc, 1, 1);
+
+ return -1;
+}
+
+
+/***************************************************************
+ * The following functions are given for completeness, but are
+ * not relevant for understanding the database processing
+ * nature of CLI
+ ***************************************************************/
+#define MAX_NUM_PRECISION 15
+/*#define max length of char string representation of no. as:
+= max(precision) + leading sign +E +expsign + max exp length
+= 15 +1 +1 +1 +2
+= 15 +5
+*/
+#define MAX_NUM_STRING_SIZE (MAX_NUM_PRECISION + 5)
+
+int build_indicator_message(SQLCHAR *errmsg, SQLPOINTER *data,
+ SQLINTEGER collen, SQLINTEGER *outlen,
+ SQLSMALLINT colnum) {
+ if (*outlen == SQL_NULL_DATA) {
+ (void)strcpy((char *)data, "NULL");
+ *outlen=4;
+ } else {
+ sprintf((char *)errmsg+strlen((char *)errmsg),
+ "%ld chars truncated, col %d\n", *outlen-collen+1,
+ colnum);
+ *outlen=255;
+ }
+ return 0;
+}
+
+
+SQLINTEGER display_length(SQLSMALLINT coltype, SQLINTEGER collen,
+ SQLCHAR *colname) {
+ switch (coltype) {
+ case SQL_VARCHAR:
+ case SQL_CHAR:
+ case SQL_VARBINARY:
+ case SQL_BINARY:
+ case SQL_BLOB:
+ case SQL_CLOB:
+ case SQL_BIT:
+ //case SQL_REF:
+ //case SQL_BIT_VARYING:
+ return(max(collen,(SQLINTEGER) strlen((char *)colname))+1);
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_NUMERIC:
+ case SQL_REAL:
+ case SQL_DECIMAL:
+ return(max(MAX_NUM_STRING_SIZE,strlen((char *)colname))+1);
+ case SQL_TYPE_DATE:
+ case SQL_TYPE_TIME:
+ //case SQL_TYPE_TIME_WITH_TIMEZONE:
+ case SQL_TYPE_TIMESTAMP:
+ //case SQL_TYPE_TIMESTAMP_WITH_TIMEZONE:
+ case SQL_INTERVAL_YEAR:
+ case SQL_INTERVAL_MONTH:
+ case SQL_INTERVAL_DAY:
+ case SQL_INTERVAL_HOUR:
+ case SQL_INTERVAL_MINUTE:
+ case SQL_INTERVAL_SECOND:
+ case SQL_INTERVAL_YEAR_TO_MONTH:
+ case SQL_INTERVAL_DAY_TO_HOUR:
+ case SQL_INTERVAL_DAY_TO_MINUTE:
+ case SQL_INTERVAL_DAY_TO_SECOND:
+ case SQL_INTERVAL_HOUR_TO_MINUTE:
+ case SQL_INTERVAL_HOUR_TO_SECOND:
+ case SQL_INTERVAL_MINUTE_TO_SECOND:
+ return(max(collen,(SQLINTEGER) strlen((char *)colname))+1);
+ case SQL_INTEGER:
+ //case SQL_BLOB_LOCATOR:
+ //case SQL_CLOB_LOCATOR:
+ //case SQL_UDT_LOCATOR:
+ //case SQL_ARRAY_LOCATOR:
+ return(max(11,strlen((char *)colname))+1);
+ case SQL_BIGINT:
+ return(max(21,strlen((char *)colname))+1);
+ case SQL_SMALLINT:
+ return(max(5,strlen((char *)colname))+1);
+ default:
+ (void)printf("Unknown datatype, %d\n", coltype);
+ return(0);
+ }
+}
+
+struct Con {
+ const char* dsn;
+ SQLHENV henv;
+ SQLHDBC hdbc;
+ Con(const char* _dsn) :
+ dsn(_dsn), henv(SQL_NULL_HANDLE), hdbc(SQL_NULL_HANDLE) {}
+};
+
+static int
+do_connect(Con& con)
+{
+ int ret;
+
+ // allocate an environment handle
+ ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &con.henv);
+ if (ret != SQL_SUCCESS)
+ return -1;
+
+ // set odbc version (required)
+ ret = SQLSetEnvAttr(con.henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
+ if (ret != SQL_SUCCESS)
+ return -1;
+
+ // allocate a connection handle
+ ret = SQLAllocHandle(SQL_HANDLE_DBC, con.henv, &con.hdbc);
+ if (ret != SQL_SUCCESS)
+ return -1;
+
+ // connect to database
+ SQLCHAR szConnStrOut[256];
+ SQLSMALLINT cbConnStrOut;
+ ret = SQLDriverConnect(con.hdbc, 0, (SQLCHAR*)con.dsn, SQL_NTS,
+ szConnStrOut, sizeof(szConnStrOut), &cbConnStrOut, SQL_DRIVER_COMPLETE);
+ if (ret != SQL_SUCCESS) {
+ ndbout << "Connection failure: Could not connect to database" << endl;
+ print_err(SQL_HANDLE_DBC, con.hdbc);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+do_disconnect(Con& con)
+{
+ // disconnect from database
+ SQLDisconnect(con.hdbc);
+
+ // free connection handle
+ SQLFreeHandle(SQL_HANDLE_DBC, con.hdbc);
+ con.hdbc = SQL_NULL_HANDLE;
+
+ // free environment handle
+ SQLFreeHandle(SQL_HANDLE_ENV, con.henv);
+ con.henv = SQL_NULL_HANDLE;
+
+ return 0;
+}
+
+static int
+get_autocommit(Con& con)
+{
+ int ret;
+ SQLUINTEGER v;
+ ret = SQLGetConnectAttr(con.hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&v, SQL_IS_UINTEGER, 0);
+ if (ret != SQL_SUCCESS) {
+ ndbout << "Get autocommit failed" << endl;
+ print_err(SQL_HANDLE_DBC, con.hdbc);
+ return -1;
+ }
+ return v;
+}
+
+static int
+set_autocommit(Con& con, SQLUINTEGER v)
+{
+ int ret;
+ ret = SQLSetConnectAttr(con.hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)v, SQL_IS_UINTEGER);
+ if (ret != SQL_SUCCESS) {
+ ndbout << "Set autocommit failed" << endl;
+ print_err(SQL_HANDLE_DBC, con.hdbc);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+do_commit(Con& con)
+{
+ int ret = SQLEndTran(SQL_HANDLE_DBC, con.hdbc, SQL_COMMIT);
+ if (ret != SQL_SUCCESS) {
+ ndbout << "Commit failed" << endl;
+ print_err(SQL_HANDLE_DBC, con.hdbc);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+do_rollback(Con& con)
+{
+ int ret = SQLEndTran(SQL_HANDLE_DBC, con.hdbc, SQL_ROLLBACK);
+ if (ret != SQL_SUCCESS) {
+ ndbout << "Rollback failed" << endl;
+ print_err(SQL_HANDLE_DBC, con.hdbc);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+do_stmt(Con& con, const char *sqlstr)
+{
+ SQLHSTMT hstmt;
+ SQLCHAR errmsg[256];
+ SQLCHAR colname[32];
+ SQLSMALLINT coltype;
+ SQLSMALLINT colnamelen;
+ SQLSMALLINT nullable;
+ SQLUINTEGER collen[MAXCOLS];
+ SQLSMALLINT scale;
+ SQLINTEGER outlen[MAXCOLS];
+ SQLCHAR *data[MAXCOLS];
+ SQLSMALLINT nresultcols = 0;
+ SQLINTEGER rowcount;
+ SQLINTEGER stmttype;
+ SQLRETURN rc;
+
+ /* allocate a statement handle */
+ SQLAllocHandle(SQL_HANDLE_STMT, con.hdbc, &hstmt);
+
+ /* execute the SQL statement */
+ rc = SQLExecDirect(hstmt, (SQLCHAR*)sqlstr, SQL_NTS);
+ if (rc == SQL_ERROR) {
+ ndbout << "Operation failed" << endl;
+ print_err(SQL_HANDLE_STMT, hstmt);
+ return -1;
+ }
+ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA_FOUND) {
+ ndbout << "Operation returned unknown code " << rc << endl;
+ return -1;
+ }
+
+ /* see what kind of statement it was */
+ SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0,
+ SQL_DIAG_DYNAMIC_FUNCTION_CODE,
+ (SQLPOINTER)&stmttype, SQL_IS_INTEGER, (SQLSMALLINT *)NULL);
+
+ switch (stmttype) {
+ /* SELECT statement */
+ case SQL_DIAG_SELECT_CURSOR:
+ /* determine number of result columns */
+ SQLNumResultCols(hstmt, &nresultcols);
+
+ /***********************
+ * Display column names
+ ***********************/
+ /* Print vertical divider */
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ SQLDescribeCol(hstmt, i+1, colname, sizeof(colname),
+ &colnamelen, &coltype, &collen[i], &scale, &nullable);
+ collen[i] = display_length(coltype, collen[i], colname);
+ for (SQLUINTEGER j=0; j<collen[i]; j++) printf("-");
+ printf("--+");
+ }
+ printf("\n");
+
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ SQLDescribeCol(hstmt, i+1, colname, sizeof(colname),
+ &colnamelen, &coltype, &collen[i], &scale, &nullable);
+
+ /* assume there is a display_length function which
+ computes correct length given the data type */
+ collen[i] = display_length(coltype, collen[i], colname);
+ (void)printf(" %*.*s |", (int)collen[i], (int)collen[i], (char *)colname);
+
+ /* allocate memory to bind column */
+ data[i] = (SQLCHAR *) malloc(collen[i]);
+ if (data[i] == NULL) {
+ ndbout << "Failed to allocate malloc memory in NDB SQL program"
+ << endl;
+ exit(-1);
+ }
+
+ /* bind columns to program vars, converting all types to CHAR */
+ SQLBindCol(hstmt, i+1, SQL_C_CHAR, data[i], collen[i], &outlen[i]);
+ }
+ printf("\n");
+
+ /* Print vertical divider */
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ SQLDescribeCol(hstmt, i+1, colname, sizeof(colname),
+ &colnamelen, &coltype, &collen[i], &scale, &nullable);
+ collen[i] = display_length(coltype, collen[i], colname);
+ for (SQLUINTEGER j=0; j<collen[i]; j++) printf("-");
+ printf("--+");
+ }
+ printf("\n");
+
+ /**********************
+ * Display result rows
+ **********************/
+ {
+ int no_of_rows_fetched=0;
+ while (1) {
+ rc=SQLFetch(hstmt);
+ errmsg[0] = '\0';
+ if (rc == SQL_ERROR) {
+ print_err(SQL_HANDLE_STMT, hstmt);
+ break;
+ }
+ if (rc == SQL_NO_DATA) break;
+ if (rc == SQL_SUCCESS) {
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ if (outlen[i] == SQL_NULL_DATA
+ || outlen[i] >= (SQLINTEGER) collen[i])
+ build_indicator_message(errmsg,
+ (SQLPOINTER *)data[i], collen[i],
+ &outlen[i], i);
+ (void)printf(" %*.*s |", (int)collen[i], (int)collen[i],
+ (char *)data[i]);
+ }
+ /* print any truncation messages */
+ (void)printf("\n%s", (char *)errmsg);
+ } else if (rc == SQL_SUCCESS_WITH_INFO) {
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ if (outlen[i] == SQL_NULL_DATA
+ || outlen[i] >= (SQLINTEGER) collen[i])
+ build_indicator_message(errmsg,
+ (SQLPOINTER *)data[i], collen[i],
+ &outlen[i], i);
+ (void)printf(" %*.*s |", (int)collen[i], (int)collen[i],
+ (char *)data[i]);
+ } /* for all columns in this row */
+ /* print any truncation messages */
+ (void)printf("\n%s", (char *)errmsg);
+ }
+ no_of_rows_fetched++;
+ } /* while rows to fetch */
+ /* Print vertical divider */
+ printf("|");
+ for (int i=0; i<nresultcols; i++) {
+ SQLDescribeCol(hstmt, i+1, colname, sizeof(colname),
+ &colnamelen, &coltype, &collen[i], &scale, &nullable);
+ collen[i] = display_length(coltype, collen[i], colname);
+ for (SQLUINTEGER j=0; j<collen[i]; j++) printf("-");
+ printf("--+");
+ }
+ printf("\n");
+ ndbout << no_of_rows_fetched << " rows fetched" << endl;
+ }
+ SQLCloseCursor(hstmt);
+ break;
+ /* searched DELETE, INSERT or searched UPDATE statement */
+ case SQL_DIAG_DELETE_WHERE:
+ case SQL_DIAG_INSERT:
+ case SQL_DIAG_UPDATE_WHERE:
+ /* check rowcount */
+ SQLRowCount(hstmt, (SQLINTEGER*)&rowcount);
+ ndbout << (int)rowcount << " rows affected" << endl;
+ break;
+ /* other statements */
+ case SQL_DIAG_ALTER_TABLE:
+ case SQL_DIAG_CREATE_TABLE:
+ case SQL_DIAG_CREATE_VIEW:
+ case SQL_DIAG_DROP_TABLE:
+ case SQL_DIAG_DROP_VIEW:
+ case SQL_DIAG_CREATE_INDEX:
+ case SQL_DIAG_DROP_INDEX:
+ case SQL_DIAG_DYNAMIC_DELETE_CURSOR:
+ case SQL_DIAG_DYNAMIC_UPDATE_CURSOR:
+ case SQL_DIAG_GRANT:
+ case SQL_DIAG_REVOKE:
+ ndbout << "Operation successful" << endl;
+ break;
+ /* implementation-defined statement */
+ default:
+ (void)printf("Unknown Statement type=%ld\n", stmttype);
+ break;
+ }
+
+ /* free data buffers */
+ for (int i=0; i<nresultcols; i++) {
+ (void)free(data[i]);
+ }
+
+ SQLFreeHandle(SQL_HANDLE_STMT, hstmt); // free statement handle
+ return(0);
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Help
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+
+void print_help() {
+ ndbout << "Commands:" << endl
+ << "set Print currect settings" << endl
+ << "set trace N Set NDB ODBC trace level to N (0-5)" << endl
+ << "set autocommit on Commit each statement (default)" << endl
+ << "set autocommit off Use explicit commit/rollback - may time out!" << endl
+ << "commit Commit changes to database" << endl
+ << "rollback Rollback (undo) any changes" << endl
+ << "whenever sqlerror Define action: exit or continue (default)" << endl
+ << endl
+ << "help Print this help" << endl
+ << "help create Print create table examples" << endl
+ << "help insert Print insert examples" << endl
+ << "help select Print select examples" << endl
+ << "help delete Print delete examples" << endl
+ << "help update Print update examples" << endl
+ << "help virtual Print help on NDB ODBC virtual tables" << endl
+ << "list tables Lists all table names" << endl
+ << endl
+ << "All other commands are sent to the NDB ODBC SQL executor"
+ << endl << endl;
+}
+
+void print_help_create() {
+ ndbout << "Create Table Examples" << endl << endl
+ << "create table t ( a integer not null, b char(20) not null," << endl
+ << " c float, primary key(a, b) )" << endl
+ << "create table t ( ndb$tid bigint unsigned primary key," << endl
+ << " b char(20) not null, c float )" << endl
+ << "create table t ( a int auto_increment primary key," << endl
+ << " b char(20) not null, c float )" << endl
+ << "create table t ( a int primary key," << endl
+ << " b int default 100 )" << endl
+ << endl
+ << "For more information read NDB Cluster ODBC Manual."
+ << endl;
+}
+
+void print_help_insert() {
+ ndbout << "Insert Examples" << endl << endl
+ << "insert into t(a, c) values (123, 'abc')" << endl
+ << "insert into t1(a, c) select a + 10 * b, c from t2" << endl
+ << "insert into t values(null, 'abc', 1.23)" << endl
+ << "insert into t(b, c) values('abc', 1.23)" << endl
+ << endl
+ << "For more information read NDB Cluster ODBC Manual."
+ << endl;
+}
+
+void print_help_select() {
+ ndbout << "Select Examples" << endl << endl
+ << "select a + b * c from t where a <= b + c and (b > c or c > 10)"
+ << endl
+ << "select a.x, b.y, c.z from t1 a, t2 b, t2 c where a.x + b.y < c.z"
+ << endl
+ << "select * from t1, t2 where a1 > 5 order by b1 + b2, c1 desc"
+ << endl
+ << "select count(*), max(a), 1 + sum(b) + avg(c * d) from t" << endl
+ << "select * from t where a < 10 or b > 10" << endl
+ << "select * from t where pk = 5 and b > 10" << endl
+ << "select * from t1, t2, t3 where t1.pk = t2.x and t2.pk = t3.y"
+ << endl << endl
+ << "For more information read NDB Cluster ODBC Manual."
+ << endl;
+}
+
+void print_help_update() {
+ ndbout << "Update and Delete Examples" << endl << endl
+ << "update t set a = b + 5, c = d where c > 10" << endl
+ << "update t set a = b + 5, c = d where pk = 5 and c > 10" << endl
+ << "update t set a = 5, c = 7 where pk = 5" << endl
+ << "delete from t where c > 10" << endl
+ << "delete from t where pk = 5 and c > 10" << endl
+ << "delete from t where pk = 5" << endl
+ << endl
+ << "For more information read NDB Cluster ODBC Manual."
+ << endl;
+}
+
+void print_help_virtual() {
+ ndbout << "Virtual tables" << endl << endl
+ << "* DUAL"
+ << " a 1-row table - example: select SYSDATE from DUAL" << endl
+ << "* ODBC$TYPEINFO" << endl
+ << " corresponds to SQLGetTypeInfo" << endl
+ << "* ODBC$TABLES" << endl
+ << " corresponds to SQLTables (ordered by NDB table id)" << endl
+ << "* ODBC$COLUMNS" << endl
+ << " corresponds to SQLColumns (ordered by NDB table id)" << endl
+ << "* ODBC$PRIMARYKEYS" << endl
+ << " corresponds to SQLPrimaryKeys (ordered by NDB table id)" << endl
+ << endl
+ << "For more information read NDB Cluster ODBC Manual."
+ << endl;
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Main
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+
+int main(int argc, const char** argv)
+{
+ ndb_init();
+ const char* usage = "Usage: ndbsql [-h] [-d dsn] [-f file] [stmt]\n-h help\n-d <database name or connect string>\n-f <file name> batch mode\nstmt single SQL statement\n";
+ const char* dsn = "TEST_DB";
+ bool helpFlg = false, batchMode = false;
+ const char* fileName = 0;
+ FILE* inputFile = stdin;
+ const char* singleStmt = 0;
+
+ s_readBuf = (char*)malloc(s_bufSize);
+ while (++argv, --argc > 0) {
+ const char* arg = argv[0];
+ if (arg[0] != '-')
+ break;
+ if (strcmp(arg, "-d") == 0) {
+ if (++argv, --argc > 0) {
+ dsn = argv[0];
+ continue;
+ }
+ }
+ if (strcmp(arg, "-h") == 0) {
+ helpFlg = true;
+ continue;
+ }
+ if (strcmp(arg, "-f") == 0) {
+ if (++argv, --argc > 0) {
+ fileName = argv[0];
+ continue;
+ }
+ }
+ ndbout << usage;
+ return 1;
+ }
+ if (helpFlg) {
+ ndbout << usage << "\n";
+ print_help();
+ return 0;
+ }
+ if (fileName != 0) {
+ if (argc > 0) {
+ ndbout << usage;
+ return 1;
+ }
+ if ((inputFile = fopen(fileName, "r")) == 0) {
+ ndbout << "Could not read file " << fileName << ": " << strerror(errno) << endl;
+ return 1;
+ }
+ batchMode = true;
+ }
+ if (argc > 0) {
+ singleStmt = argv[0];
+ batchMode = true;
+ }
+ if (! batchMode)
+ ndbout << "NDB Cluster NDB SQL -- A simple SQL Command-line Interface\n\n";
+
+ Con con(dsn);
+ if (do_connect(con) < 0)
+ return 1;
+ if (! batchMode)
+ ndbout << "Terminate SQL statements with a semi-colon ';'\n";
+
+ char* line = 0;
+ char* line2 = 0;
+ char* line3 = 0;
+ unsigned lineno = 0;
+ bool has_semi;
+ bool exit_on_error = false;
+ int exit_code = 0;
+ while (1) {
+ free(line);
+ line = 0;
+ lineno = 0;
+
+more_lines:
+ free(line2);
+ free(line3);
+ line2 = line3 = 0;
+ lineno++;
+ has_semi = false;
+ char prompt[20];
+ if (lineno == 1)
+ strcpy(prompt, "SQL> ");
+ else
+ sprintf(prompt, "%4d ", lineno);
+ if (singleStmt != 0) {
+ line = strdup(singleStmt);
+ int n = strlen(line);
+ while (n > 0 && isspace(line[n - 1])) {
+ line[--n] = 0;
+ }
+ if (n > 0 && line[n - 1] == ';')
+ line[n - 1] = 0;
+ has_semi = true; // regardless
+ } else {
+ const char *line1 = readline_gets(prompt, batchMode, inputFile);
+ if (line1 != 0) {
+ if (line == 0)
+ line = strdup(line1);
+ else {
+ line = (char*)realloc(line, strlen(line) + 1 + strlen(line1) + 1);
+ strcat(line, "\n");
+ strcat(line, line1);
+ }
+ if (batchMode)
+ ndbout << prompt << line1 << endl;
+ } else {
+ if (! batchMode)
+ ndbout << endl;
+ if (line != 0)
+ ndbout << "Ignored unterminated SQL statement" << endl;
+ break;
+ }
+ }
+
+ line2 = (char*)malloc(strlen(line) + 1);
+ {
+ char* p = line2;
+ char* q = line;
+ bool str = false;
+ while (*q != 0) {
+ if (*q == '\'') {
+ str = !str;
+ *p++ = *q++;
+ } else if (!str && *q == '-' && *(q + 1) == '-') {
+ while (*q != 0 && *q != '\n')
+ q++;
+ } else
+ *p++ = *q++;
+ }
+ *p = 0;
+ int n = strlen(line2);
+ while (n > 0 && isspace(line2[n - 1]))
+ line2[--n] = 0;
+ if (n > 0 && line2[n - 1] == ';') {
+ line2[--n] = 0;
+ has_semi = true;
+ }
+ }
+ line3 = strdup(line2);
+ char* tok[10];
+ int ntok = 0;
+ tok[ntok] = strtok(line3, " ");
+ while (tok[ntok] != 0) {
+ ntok++;
+ if (ntok == 10)
+ break;
+ tok[ntok] = strtok(0, " ");
+ }
+ if (ntok == 0)
+ continue;
+
+ if (!strcasecmp(tok[0], "help") || !strcmp(tok[0], "?")) {
+ if (ntok != 2)
+ print_help();
+ else if (!strcasecmp(tok[1], "create"))
+ print_help_create();
+ else if (!strcasecmp(tok[1], "insert"))
+ print_help_insert();
+ else if (strcasecmp(tok[1], "select"))
+ print_help_select();
+ else if (!strcasecmp(tok[1], "delete"))
+ print_help_update();
+ else if (!strcasecmp(tok[1], "update"))
+ print_help_update();
+ else if (!strcasecmp(tok[1], "virtual"))
+ print_help_virtual();
+ else
+ print_help();
+ continue;
+ }
+
+ if (!strcasecmp(tok[0], "list")) {
+ if (ntok == 2 && !strcasecmp(tok[1], "tables")) {
+ free(line2);
+ line2 = strdup("SELECT TABLE_NAME FROM ODBC$TABLES");
+ has_semi = true;
+ } else {
+ ndbout << "Invalid list option - try help" << endl;
+ continue;
+ }
+ }
+
+ if (ntok == 1 && !strcasecmp(tok[0], "quit"))
+ break;
+ if (ntok == 1 && !strcasecmp(tok[0], "exit"))
+ break;
+ if (ntok == 1 && !strcasecmp(tok[0], "bye"))
+ break;
+
+ if (!strcasecmp(tok[0], "set")) {
+ if (ntok == 1) {
+ char* p;
+ p = getenv("NDB_ODBC_TRACE");
+ ndbout << "Trace level is " << (p ? atoi(p) : 0) << endl;
+ int ret = get_autocommit(con);
+ if (ret != -1)
+ ndbout << "Autocommit is " << (ret == SQL_AUTOCOMMIT_ON ? "on" : "off") << endl;
+ } else if (ntok == 3 && !strcasecmp(tok[1], "trace")) {
+ static char env[40];
+ int n = tok[2] ? atoi(tok[2]) : 0;
+ sprintf(env, "NDB_ODBC_TRACE=%d", n);
+ putenv(env);
+ ndbout << "Trace level set to " << n << endl;
+ } else if (ntok == 3 && !strcasecmp(tok[1], "autocommit")) {
+ if (tok[2] && !strcasecmp(tok[2], "on")) {
+ int ret = set_autocommit(con, SQL_AUTOCOMMIT_ON);
+ if (ret != -1)
+ ndbout << "Autocommit set to ON" << endl;
+ } else if (tok[2] && !strcasecmp(tok[2], "off")) {
+ int ret = set_autocommit(con, SQL_AUTOCOMMIT_OFF);
+ if (ret != -1)
+ ndbout << "Autocommit set to OFF - transaction may time out" << endl;
+ } else {
+ ndbout << "Invalid autocommit option - try help" << endl;
+ }
+ } else {
+ ndbout << "Invalid set command - try help" << endl;
+ }
+ continue;
+ }
+
+ if (ntok >= 2 &&
+ !strcasecmp(tok[0], "whenever") && !strcasecmp(tok[1], "sqlerror")) {
+ if (ntok == 3 && !strcasecmp(tok[2], "exit"))
+ exit_on_error = true;
+ else if (ntok == 3 && !strcasecmp(tok[2], "continue"))
+ exit_on_error = false;
+ else {
+ ndbout << "Invalid whenever clause - try help" << endl;
+ }
+ continue;
+ }
+
+ if (!strcasecmp(tok[0], "commit")) {
+ if (ntok == 1) {
+ if (do_commit(con) != -1)
+ ndbout << "Commit done" << endl;
+ else {
+ exit_code = 1;
+ if (exit_on_error) {
+ ndbout << "Exit on error" << endl;
+ break;
+ }
+ }
+ } else {
+ ndbout << "Invalid commit command - try help" << endl;
+ }
+ continue;
+ }
+
+ if (!strcasecmp(tok[0], "rollback")) {
+ if (ntok == 1) {
+ if (do_rollback(con) != -1)
+ ndbout << "Rollback done" << endl;
+ else {
+ exit_code = 1;
+ if (exit_on_error) {
+ ndbout << "Exit on error" << endl;
+ break;
+ }
+ }
+ } else {
+ ndbout << "Invalid commit command - try help" << endl;
+ }
+ continue;
+ }
+
+ if (! has_semi)
+ goto more_lines;
+ if (do_stmt(con, line2) != 0) {
+ exit_code = 1;
+ if (exit_on_error) {
+ ndbout << "Exit on error" << endl;
+ break;
+ }
+ }
+ if (singleStmt)
+ break;
+ }
+ do_disconnect(con);
+ return exit_code;
+}
+
+// vim: set sw=2 et:
diff --git a/storage/ndb/tools/old_dirs/copy_tab/Makefile b/storage/ndb/tools/old_dirs/copy_tab/Makefile
new file mode 100644
index 00000000000..4ad33a26652
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/copy_tab/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := copy_tab
+
+SOURCES := copy_tab.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/cpcc/Makefile b/storage/ndb/tools/old_dirs/cpcc/Makefile
new file mode 100644
index 00000000000..78f8c61e464
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/cpcc/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE = util
+
+BIN_TARGET = ndb_cpcc
+
+SOURCES = cpcc.cpp
+OBJECTS_LOC = $(call fixpath,$(NDB_TOP)/src/mgmclient/CpcClient.o)
+
+CFLAGS_cpcc.cpp := -I$(call fixpath,$(NDB_TOP)/src/mgmclient)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/create_index/Makefile b/storage/ndb/tools/old_dirs/create_index/Makefile
new file mode 100644
index 00000000000..38f2df970c4
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/create_index/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := create_index
+
+# Source files of non-templated classes (.C files)
+SOURCES = create_index.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/tools/old_dirs/delete_all/Makefile b/storage/ndb/tools/old_dirs/delete_all/Makefile
new file mode 100644
index 00000000000..1cae240eb8f
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/delete_all/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := delete_all
+
+SOURCES := delete_all.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/desc/Makefile b/storage/ndb/tools/old_dirs/desc/Makefile
new file mode 100644
index 00000000000..614984cfd35
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/desc/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := desc
+
+SOURCES := desc.C
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/drop_index/Makefile b/storage/ndb/tools/old_dirs/drop_index/Makefile
new file mode 100644
index 00000000000..969bee51064
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/drop_index/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := drop_index
+
+# Source files of non-templated classes (.C files)
+SOURCES = drop_index.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/tools/old_dirs/drop_tab/Makefile b/storage/ndb/tools/old_dirs/drop_tab/Makefile
new file mode 100644
index 00000000000..d7b21fe982c
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/drop_tab/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := drop_tab
+
+# Source files of non-templated classes (.C files)
+SOURCES = drop_tab.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/tools/old_dirs/list_tables/Makefile b/storage/ndb/tools/old_dirs/list_tables/Makefile
new file mode 100644
index 00000000000..b60f161ee68
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/list_tables/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = list_tables
+
+SOURCES = listTables.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/ndbnet/Makefile.PL b/storage/ndb/tools/old_dirs/ndbnet/Makefile.PL
new file mode 100644
index 00000000000..4b27a17de15
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/Makefile.PL
@@ -0,0 +1,158 @@
+# -*- perl -*-
+
+use strict;
+use Config;
+use ExtUtils::MakeMaker qw(WriteMakefile);
+use Test::Harness;
+
+require 5.005;
+
+my $base;
+if ($base ||= $ENV{NDB_BASE}) {
+ warn "Using NDB_BASE=$base\n";
+}
+$base or die "FATAL: need env.variable NDB_BASE\n";
+
+my $top;
+if ($top ||= $ENV{NDB_TOP}) {
+ warn "Using NDB_TOP=$top\n";
+}
+$top or die "FATAL: need env.variable NDB_TOP\n";
+
+my @scripts = qw(ndbnet.pl ndbnetd.pl);
+
+for my $f (@scripts) {
+ my $p = $f;
+ $p =~ s/\.pl$//;
+ unlink("$p.sh");
+ open(G, ">$p.sh") or die "$p.sh: $!";
+ if ($Config{osname} ne 'MSWin32') {
+ print G <<END;
+#! /bin/sh
+
+# installed in \$NDB_BASE
+# selects which $p to run (normally from latest release)
+# created in source directory by "make install-base"
+
+NDB_BASE=$base
+export NDB_BASE
+
+PATH=\$NDB_BASE/bin:\$PATH
+export PATH
+
+LD_LIBRARY_PATH=\$NDB_BASE/lib:\$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+PERL5LIB=\$NDB_BASE/lib/perl5:\$PERL5LIB
+export PERL5LIB
+
+NDB_TOP=$top
+export NDB_TOP
+
+PATH=\$NDB_TOP/bin:\$PATH
+export PATH
+
+LD_LIBRARY_PATH=\$NDB_TOP/lib:\$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+PERL5LIB=\$NDB_TOP/lib/perl5:\$PERL5LIB
+export PERL5LIB
+
+exec perl \$NDB_TOP/lib/perl5/$p.pl "\$@"
+END
+ } else {
+ print G <<END;
+rem installed in \$NDB_BASE
+rem selects which $p to run (normally from latest release)
+rem created in source directory by "make install-base"
+
+set NDB_BASE=$base
+set PATH=%NDB_BASE%\\bin;%PATH%
+set PERL5LIB=%NDB_BASE%\\lib\\perl5;%PERL5LIB%
+set NDB_TOP=$top
+set PATH=%NDB_TOP%\\bin;%PATH%
+set PERL5LIB=%NDB_TOP%\\lib\\perl5;%PERL5LIB%
+perl %NDB_TOP%\\lib\\perl5\\$p.pl %1 %2 %3 %4 %5 %6 %7 %8 %9
+END
+ }
+ close G;
+}
+
+unshift(@INC, 'lib');
+$main::onlymodules = 1;
+require lib::NDB::Util;
+require lib::NDB::Net;
+require lib::NDB::Run;
+
+my @modules = (
+ q(NDB::Util),
+ @NDB::Util::modules,
+ q(NDB::Net),
+ @NDB::Net::modules,
+ q(NDB::Run),
+ @NDB::Run::modules,
+);
+
+my @modulepaths = map { s!::!/!g; s!$!.pm!; $_ } @modules;
+
+my %pm = ();
+for my $pl (@scripts) {
+ $pm{"$pl"} = "\$(INST_LIBDIR)/$pl";
+}
+for my $pm (@modulepaths) {
+ $pm{"lib/$pm"} = "\$(INST_LIBDIR)/$pm";
+}
+
+WriteMakefile(
+ NAME=> 'NDB',
+ PM=> \%pm,
+ EXE_FILES=> [ qw(ndbrun) ],
+# install
+ PREFIX=> $top,
+ LIB=> "$top/lib/perl5",
+);
+
+sub MY::postamble {
+ my $mk = "";
+ $mk .= "\n" . <<END;
+# NDB make targets
+libs: all install
+bins:
+links:
+depend:
+clean_dep:
+#clean:
+cleanall:
+tidy:
+#distclean:
+check:
+ perl -Ilib -cw -e "use NDB::Util"
+ perl -Ilib -cw -e "use NDB::Net"
+ perl -Ilib -cw -e "use NDB::Run"
+ perl -Ilib -cw ndbnetd.pl
+ perl -Ilib -cw ndbnet.pl
+END
+ if ($Config{osname} ne 'MSWin32') {
+ $mk .= "\n" . <<END;
+# install startup scripts to \$NDB_BASE
+install-base:
+ test "\$\$NDB_BASE"
+ mkdir -p \$\$NDB_BASE/bin
+ rm -f \$\$NDB_BASE/bin/ndbnet
+ cp -p ndbnet.sh \$\$NDB_BASE/bin/ndbnet
+ chmod +x \$\$NDB_BASE/bin/ndbnet
+ rm -f \$\$NDB_BASE/bin/ndbnetd
+ cp -p ndbnetd.sh \$\$NDB_BASE/bin/ndbnetd
+ chmod +x \$\$NDB_BASE/bin/ndbnetd
+END
+ } else {
+ $mk .= "\n" . <<END;
+install-base:
+ copy ndbnet.sh $base\\bin\\ndbnet.bat
+ copy ndbnetd.sh $base\\bin\\ndbnetd.bat
+END
+ }
+ return $mk;
+}
+
+1;
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net.pm
new file mode 100644
index 00000000000..3b7b16bb3cf
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net.pm
@@ -0,0 +1,42 @@
+package NDB::Net;
+
+use strict;
+use Carp;
+require Exporter;
+
+use NDB::Util;
+
+use vars qw(@ISA @EXPORT @EXPORT_OK);
+@ISA = qw(Exporter);
+
+use vars qw(@modules);
+@modules = qw(
+ NDB::Net::Base
+ NDB::Net::Client
+ NDB::Net::Command
+ NDB::Net::Config
+ NDB::Net::Database
+ NDB::Net::Env
+ NDB::Net::Node
+ NDB::Net::NodeApi
+ NDB::Net::NodeDb
+ NDB::Net::NodeMgmt
+ NDB::Net::Server
+ NDB::Net::ServerINET
+ NDB::Net::ServerUNIX
+);
+
+return 1 if $main::onlymodules;
+
+for my $module (@modules) {
+ eval "require $module";
+ $@ and confess "$module $@";
+}
+
+for my $module (@modules) {
+ eval "$module->initmodule";
+ $@ and confess "$module $@";
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Base.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Base.pm
new file mode 100644
index 00000000000..900446138e8
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Base.pm
@@ -0,0 +1,12 @@
+package NDB::Net::Base;
+
+use strict;
+use Carp;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Client.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Client.pm
new file mode 100644
index 00000000000..d34a18d63af
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Client.pm
@@ -0,0 +1,252 @@
+package NDB::Net::Client;
+
+use strict;
+use Carp;
+use POSIX();
+use Socket;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+my %clientcache = ();
+my $clientid = 0;
+
+NDB::Net::Client->attributes(
+ id => sub { /^\d+$/ },
+ addtime => sub { /^\d+$/ },
+ state => sub { /^(new|input|cmd)$/ },
+ socket => sub { ref && $_->isa('NDB::Util::Socket') },
+ serversocket => sub { ref && $_->isa('NDB::Util::Socket') },
+ serverlock => sub { ref && $_->isa('NDB::Util::Lock') },
+ event => sub { ref && $_->isa('NDB::Util::Event') },
+ context => sub { defined },
+ cmd => sub { ref && $_->isa('NDB::Net::Command') },
+);
+
+sub desc {
+ my $client = shift;
+ my $id = $client->getid;
+ my $fileno = fileno($client->getsocket->getfh);
+ return "client $id fd=$fileno";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $client = $class->SUPER::new(%attr);
+ $client->setid(++$clientid)
+ or $log->push, return undef;
+ $client->setaddtime(time)
+ or $log->push, return undef;
+ $client->setstate(q(new))
+ or $log->push, return undef;
+ $client->setsocket($attr{socket})
+ or $log->push, return undef;
+ $client->setserversocket($attr{serversocket})
+ or $log->push, return undef;
+ $client->setserverlock($attr{serverlock})
+ or $log->push, return undef;
+ $client->setevent($attr{event})
+ or $log->push, return undef;
+ $client->setcontext($attr{context})
+ or $log->push, return undef;
+ $log->put("add")->push($client)->info;
+ $clientcache{$client->getid} = $client;
+ return $client;
+}
+
+sub listall {
+ my $class = shift;
+ my $list = [];
+ for my $id (sort { $a <=> $b } keys %clientcache) {
+ my $client = $clientcache{$id};
+ push(@$list, $client);
+ }
+ return $list;
+}
+
+sub exists {
+ my $client = shift;
+ return exists($clientcache{$client->getid});
+}
+
+sub delete {
+ my $client = shift;
+ $log->put("delete")->push($client)->info;
+ $client->getevent->clear($client->getsocket, 'r');
+ $client->getsocket->close;
+ delete $clientcache{$client->getid} or confess 'oops';
+}
+
+sub deleteother {
+ my $thisclient = shift;
+ for my $id (sort { $a <=> $b } keys %clientcache) {
+ my $client = $clientcache{$id};
+ if ($client ne $thisclient) {
+ $client->delete;
+ }
+ }
+}
+
+sub deleteall {
+ my $class = shift;
+ for my $id (sort { $a <=> $b } keys %clientcache) {
+ my $client = $clientcache{$id};
+ $client->delete;
+ }
+}
+
+# processing
+
+sub processnew {
+ my $client = shift;
+ @_ == 0 or confess 0+@_;
+ $log->put("process new")->push($client)->debug;
+ $client->getevent->set($client->getsocket, 'r');
+ $log->attachuser(io => $client->getsocket);
+ $client->setstate(q(input))
+ or $log->push, return undef;
+ return 1;
+}
+
+sub processinput {
+ my $client = shift;
+ @_ == 0 or confess 0+@_;
+ $log->put("process input")->push($client)->debug;
+ my $line = $client->getsocket->readline;
+ if (! defined($line)) {
+ $log->push;
+ return undef;
+ }
+ if (length($line) == 0) {
+ if ($client->getsocket->getreadend) {
+ $log->put("no command")->push($client);
+ return undef;
+ }
+ $log->put("wait for input")->push($client)->debug;
+ return 1;
+ }
+ $log->put("got line: $line")->push($client)->info;
+ $client->getevent->clear($client->getsocket, 'r');
+ my $cmd = NDB::Net::Command->new(line => $line)
+ or $log->push, return undef;
+ $log->put("command received")->push($cmd)->push($client)->debug;
+ $client->setcmd($cmd)
+ or $log->push, return undef;
+ $client->setstate(q(cmd))
+ or $log->push, return undef;
+ return 1;
+}
+
+sub processcmd {
+ my $client = shift;
+ @_ == 0 or confess 0+@_;
+ $log->put("process cmd")->push($client)->debug;
+ my $cmd = $client->getcmd;
+ my $context = $client->getcontext;
+ my $name_fg = "cmd_" . $cmd->getname . "_fg";
+ my $name_bg = "cmd_" . $cmd->getname . "_bg";
+ my $fg = $context->can($name_fg);
+ my $bg = $context->can($name_bg);
+ unless ($fg || $bg) {
+ $log->put("%s: unimplemented", $cmd->getname);
+ return undef;
+ }
+ my $ret;
+ if ($fg) {
+ $log->put($name_fg)->push($cmd)->push($client)->info;
+ if (! ref($context)) {
+ $ret = &$fg($cmd);
+ }
+ else {
+ $ret = &$fg($context, $cmd);
+ }
+ defined($ret)
+ or $log->push, return undef;
+ if (! $bg) {
+ $log->push($name_fg)->putvalue($ret)->user;
+ return 1;
+ }
+ }
+ if ($bg) {
+ $log->put($name_bg)->push($cmd)->push($client)->info;
+ my $pid = fork;
+ if (! defined($pid)) {
+ $log->put("fork failed: $!");
+ return undef;
+ }
+ if ($pid == 0) {
+ $client->getserversocket->close;
+ $client->getserverlock->close;
+ $client->deleteother;
+ if (! ref($context)) {
+ $ret = &$bg($cmd);
+ }
+ else {
+ $ret = &$bg($context, $cmd);
+ }
+ if (! $ret) {
+ $log->push($client)->error;
+ $log->push($name_bg)->putvalue(undef)->user;
+ exit(1);
+ }
+ $log->push($name_bg)->putvalue($ret)->user;
+ exit(0);
+ }
+ }
+ return 1;
+}
+
+sub process {
+ my $client = shift;
+ @_ == 0 or confess 0+@_;
+ try: {
+ if ($client->getstate eq q(new)) {
+ $client->processnew
+ or $log->push, last try;
+ }
+ if ($client->getstate eq q(input)) {
+ $client->processinput
+ or $log->push, last try;
+ }
+ if ($client->getstate eq q(cmd)) {
+ $client->processcmd
+ or $log->push, last try;
+ $log->detachuser;
+ $client->delete;
+ return 1;
+ }
+ return 1;
+ }
+ $log->push($client)->error;
+ $log->putvalue(undef)->user;
+ $log->detachuser;
+ $client->delete;
+ return undef;
+}
+
+sub processall {
+ my $class = shift;
+ @_ == 0 or confess 0+@_;
+ my $list = $class->listall;
+ for my $client (@$list) {
+ $client->process;
+ }
+ while ((my $pid = waitpid(-1, &POSIX::WNOHANG)) > 0) {
+ $log->put("harvested pid=$pid")->info;
+ }
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Command.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Command.pm
new file mode 100644
index 00000000000..30145d09fa9
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Command.pm
@@ -0,0 +1,641 @@
+package NDB::Net::Command;
+
+use strict;
+use Carp;
+use Getopt::Long;
+use Text::ParseWords ();
+use Text::Tabs ();
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+my($cmdtab, $aliastab);
+
+NDB::Net::Command->attributes(
+ name => sub { /^\s*\w+\b/ },
+ argv => sub { ref eq 'ARRAY' },
+ optspec => sub { ref eq 'ARRAY' },
+ argspec => sub { /^\d+$/ || ref eq 'CODE' },
+ short => sub { defined && ! ref },
+ help => sub { defined && ! ref },
+ opts => sub { ref eq 'HASH' },
+ args => sub { ref eq 'ARRAY' },
+);
+
+sub desc {
+ my $cmd = shift;
+ return "command " . $cmd->getname("?");
+};
+
+sub processname {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my $cmdargv = $cmd->getargv;
+ my $name = shift(@$cmdargv);
+ my %seen = ();
+ while ((my $entry) = grep($name eq $_->{name}, @$aliastab)) {
+ $seen{$name}++ && last;
+ unshift(@$cmdargv, split(' ', $entry->{value}));
+ $name = shift(@$cmdargv);
+ }
+ if ((my $entry) = grep($_->{name} eq $name, @$cmdtab)) {
+ $cmd->setname($entry->{name})
+ or $log->push, return undef;
+ $cmd->setoptspec($entry->{optspec})
+ or $log->push, return undef;
+ $cmd->setargspec($entry->{argspec})
+ or $log->push, return undef;
+ }
+ else {
+ $log->put("$name: undefined")->push($cmd);
+ return undef;
+ }
+ return 1;
+}
+
+sub getopttype {
+ my $cmd = shift;
+ my($key) = @_;
+ if (grep(/^$key$/, @{$cmd->getoptspec})) {
+ return 1;
+ }
+ if (grep(/^$key=/, @{$cmd->getoptspec})) {
+ return 2;
+ }
+ return undef;
+}
+
+sub processargv {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my $cmdargv = $cmd->getargv;
+ my @newargv = ();
+ while (@$cmdargv) {
+ my $v = shift(@$cmdargv);
+ if (! defined($v)) {
+ next;
+ }
+ if (ref($v) eq 'ARRAY') {
+ unshift(@$cmdargv, @$v); # push back
+ next;
+ }
+ if (ref($v) eq 'HASH') {
+ for my $k (sort keys %$v) {
+ if ($cmd->getopttype($k) == 1) {
+ push(@newargv, "--$k");
+ next;
+ }
+ if ($cmd->getopttype($k) == 2) {
+ push(@newargv, "--$k", $v->{$k});
+ next;
+ }
+ $log->put("$k: undefined option")->push($cmd);
+ return undef;
+ }
+ next;
+ }
+ if (ref($v)) {
+ confess 'oops';
+ }
+ push(@newargv, $v);
+ }
+ push(@$cmdargv, @newargv);
+ return 1;
+}
+
+sub processopts {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my $cmdargv = $cmd->getargv;
+ local(@ARGV) = @$cmdargv;
+ try: {
+ local $SIG{__WARN__} = sub {
+ my $errstr = "@_";
+ while (chomp($errstr)) {}
+ $log->put($errstr)->push($cmd);
+ };
+ $cmd->setopts({})
+ or $log->push, return undef;
+ Getopt::Long::Configure(qw(
+ default no_getopt_compat no_ignore_case
+ ));
+ GetOptions($cmd->getopts, @{$cmd->getoptspec})
+ or return undef;
+ }
+ $cmd->setargs([ @ARGV ])
+ or $log->push, return undef;
+ return 1;
+}
+
+sub processargs {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my $cmdargs = $cmd->getargs;
+ if ($cmd->getargspec =~ /^\d+$/) {
+ if (@$cmdargs != $cmd->getargspec) {
+ $log->put("invalid arg count %d != %d",
+ scalar(@$cmdargs), $cmd->getargspec)->push($cmd);
+ return undef;
+ }
+ }
+ if (ref($cmd->getargspec) eq 'CODE') {
+ local $_ = scalar(@$cmdargs);
+ if (! &{$cmd->getargspec}()) {
+ $log->put("invalid arg count %d",
+ scalar(@$cmdargs))->push($cmd);
+ return undef;
+ }
+ }
+ return 1;
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my %attr = @_;
+ my $cmd = $class->SUPER::new(%attr);
+ my $cmdargv = [];
+ $cmd->setargv($cmdargv)
+ or $log->push, return undef;
+ my $line = $attr{line};
+ my $argv = $attr{argv};
+ defined($line) != defined($argv) # exactly one
+ or confess 'oops';
+ if (defined($line)) {
+ ! ref($line) or confess 'oops';
+ push(@$cmdargv, Text::ParseWords::shellwords($line));
+ }
+ if (defined($argv)) {
+ ref($argv) eq 'ARRAY' or confess 'oops';
+ push(@$cmdargv, @$argv);
+ }
+ if (! @$cmdargv) {
+ $log->put("empty command");
+ return undef;
+ }
+ $cmd->processname
+ or $log->push, return undef;
+ $cmd->processargv
+ or $log->push, return undef;
+ $cmd->processopts
+ or $log->push, return undef;
+ $cmd->processargs
+ or $log->push, return undef;
+ return $cmd;
+}
+
+sub getline {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my @text = ($cmd->getname);
+ for my $k (sort keys %{$cmd->getopts}) {
+ if ($cmd->getopttype($k) == 1) {
+ push(@text, "--$k");
+ next;
+ }
+ if ($cmd->getopttype($k) == 2) {
+ push(@text, "--$k", quotemeta($cmd->getopts->{$k}));
+ next;
+ }
+ confess 'oops';
+ }
+ for my $s (@{$cmd->getargs}) {
+ push(@text, quotemeta($s));
+ }
+ return "@text";
+}
+
+sub setopt {
+ my $cmd = shift;
+ my($key, $value) = @_;
+ if ($cmd->getopttype($key) == 1) {
+ @_ == 1 or confess 0+@_;
+ $cmd->getopts->{$key} = 1;
+ }
+ elsif ($cmd->getopttype($key) == 2) {
+ @_ == 2 or confess 0+@_;
+ $cmd->getopts->{$key} = $value;
+ }
+ else {
+ confess 'oops';
+ }
+}
+
+sub getopt {
+ my $cmd = shift;
+ @_ == 1 or confess 0+@_;
+ my($key) = @_;
+ $cmd->getopttype($key) or confess 'oops';
+ return $cmd->getopts->{$key};
+}
+
+sub setarg {
+ my $cmd = shift;
+ @_ == 2 or confess 0+@_;
+ my($idx, $value) = @_;
+ $cmd->getargs->[$idx] = $value;
+}
+
+sub getarg {
+ my $cmd = shift;
+ @_ == 1 or confess 0+@_;
+ my($idx) = @_;
+ return $cmd->getargs->[$idx];
+}
+
+sub getarglist {
+ my $cmd = shift;
+ @_ == 1 or confess 0+@_;
+ my($idx) = @_;
+ my @args = @{$cmd->getargs};
+ @args = @args[$idx..$#args];
+ return \@args;
+}
+
+sub helptext {
+ my $cmd = shift;
+ @_ <= 1 or confess 0+@_;
+ my $name = $cmd->getargs->[0];
+ my $text = "";
+ my $indent = " "x4;
+ if (defined($name)) {
+ for my $entry (@$aliastab) {
+ if ($entry->{name} eq $name) {
+ $text .= "alias $name=\"$entry->{value}\"\n";
+ ($name) = split(' ', $entry->{value});
+ last;
+ }
+ }
+ }
+ else {
+ $text .= "COMMANDS\n";
+ }
+ for my $entry (@$cmdtab) {
+ if (defined($name)) {
+ if ($entry->{name} eq $name) {
+ $text .= uc($name) . "\n";
+ for my $t (split(/\n/, $entry->{help})) {
+ $text .= $indent;
+ $text .= Text::Tabs::expand($t) . "\n";
+ }
+ last;
+ }
+ }
+ else {
+ $text .= $indent;
+ $text .= sprintf("%-16s%s\n", $entry->{name}, $entry->{short});
+ }
+ }
+ if (! $text) {
+ $log->put("$name: undefined");
+ return undef;
+ }
+ return $text;
+}
+
+sub aliastext {
+ my $cmd = shift;
+ @_ == 0 or confess 0+@_;
+ my $text = "";
+ my $indent = " "x4;
+ $text .= "ALIASES\n";
+ for my $entry (@$aliastab) {
+ $text .= $indent;
+ $text .= sprintf("%-16s%s\n", $entry->{name}, $entry->{value});
+ }
+ return $text;
+}
+
+# commands
+# name command name (unique)
+# optspec option spec in Getopt::Long style
+# argspec arg count (number or sub)
+# short one line summary
+# help long help text
+# opts options HASH (after parse)
+# args arguments ARRAY (after parse)
+
+$cmdtab = [
+ {
+ name => "help",
+ optspec => [ qw() ],
+ argspec => sub { $_[0] <= 1 },
+ short => "print help (try: h h)",
+ help => <<END,
+help [name]
+name command name or alias
+
+Print help summary or longer help text for one command.
+
+General:
+
+Options can be placed anywhere on command line and can be abbreviated.
+Example: "start db11 -i" instead of "start --init_rm db11".
+
+Several commands have internal option --local which makes current server
+do the work, instead of passing it to other servers. This option should
+not be used explicitly, except for testing.
+END
+ },
+ {
+ name => "alias",
+ optspec => [ qw() ],
+ argspec => 0,
+ short => "list aliases",
+ help => <<END,
+alias
+
+List built-in aliases. New ones cannot be defined (yet).
+END
+ },
+ {
+ name => "quit",
+ optspec => [ qw() ],
+ argspec => 0,
+ short => "exit ndbnet",
+ help => <<END,
+quit
+
+Exit ndbnet client.
+END
+ },
+ {
+ name => "server",
+ optspec => [ qw(all direct pass parallel script=s local) ],
+ argspec => sub { $_ >= 1 },
+ short => "net server commands",
+ help => <<END,
+server action id... [options]
+action start restart stop ping
+id net server id from net config
+--all do all servers listed in net config
+--direct do not use a server
+--pass pass current ndb environment to remote command
+--parallel run in parallel when possible
+--script path remote script instead of "ndbnetd"
+--local for internal use by servers
+
+Each host needs one net server (ndbnetd). It should be started
+from latest ndb installation, for example at system boot time.
+A "server ping" is used to check that all servers are up (option
+--all is added if no server ids are given).
+
+Other actions are mainly for testing. A "server start" tries to
+start servers via "ssh". This does not work if "ssh" is not allowed
+or if the remote command does not get right environment.
+
+Option --direct makes this ndbnet client do the work. It is assumed
+for "server start" and it requires that a local net config exists.
+Option --pass is useful in a homogeneous (NFS) environment.
+
+There are aliases "startserver" for "server start", etc.
+END
+ },
+ {
+ name => "start",
+ optspec => [ qw(init_rm nostart stop kill config old home=s clean proxy=s) ],
+ argspec => 1,
+ short => "start database",
+ help => <<END,
+start dbname [options]
+dbname database name
+--init_rm destroy existing database files on each node
+--nostart for DB nodes only do "ndb -n"
+--stop do "stop dbname" first
+--kill do "kill dbname" first
+--config create run config but start no processes
+--old use existing config files
+--home dir override home (product dir) from config
+--clean passed to startnode
+--proxy list generate proxy ports (read the source)
+
+Start a database as follows:
+
+- start mgmt servers on all mgmt nodes
+- start ndb processes on all db nodes
+- send "all start" to first mgmt server (redundant)
+- start processes on all api nodes (if runtype!="manual")
+
+Older database versions (v1.0) are started similarly except that there
+are no management servers.
+
+The --proxy option is used for testing network problems.
+END
+ },
+ {
+ name => "startnode",
+ optspec => [ qw(init_rm nostart config old run=s home=s local clean proxy=s) ],
+ argspec => 2,
+ short => "start database node",
+ help => <<END,
+startnode dbname nodeid [options]
+dbname database name
+nodeid node number
+--init_rm destroy existing database files (if db node)
+--nostart if DB node only do "ndb -n"
+--config create run config but start no processes
+--old use existing config files
+--run cmd run this shell command, default from config file
+--home dir override home (product dir) from config
+--local node must be local to this ndbnet server
+--clean remove old node dir first
+--proxy list processed by mgmt nodes, see "start" command
+
+Start the process on one database node. The node can be of any type
+(mgmt/db/api). If already running, does nothing.
+
+The --run option specifies a simple shell command (not pipeline etc).
+Defaults:
+
+- mgmt node => mgmtsrvr -p port -l Ndb.cfg -i config.txt -c config.bin
+ where port comes from ndbnet.xml
+- db node => ndb
+- api node => based on ndbnet config, default empty
+
+The node server exits when the command exits (unless runtype is set to
+auto). Command exit status is not available.
+
+Used internally by db "start" command.
+END
+ },
+ {
+ name => "stop",
+ optspec => [ qw() ],
+ argspec => 1,
+ short => "stop database",
+ help => <<END,
+stop dbname [options]
+dbname database name
+
+Stop a database as follows (see also "stopnode" command):
+
+- send SIGTERM to api processes, wait for them to exit
+- send "all stop" command to first mgmt server
+- wait for db processes to exit
+- send "quit" to mgmt servers, wait for them to exit
+END
+ },
+ {
+ name => "stopnode",
+ optspec => [ qw(local) ],
+ argspec => 2,
+ short => "stop process on one node",
+ help => <<END,
+stopnode dbname nodeid [options]
+dbname database name
+nodeid node number
+--local node must be local to this server
+
+Stop process on one database node. Action depends on node type:
+
+- api node: send SIGTERM to the process, wait for it to exit
+- db node: no action, wait for the ndb process to exit
+- mgmt node: send "quit" command to mgmt server, wait for it to exit
+
+Used internally by db "stop" command.
+END
+ },
+ {
+ name => "kill",
+ optspec => [ qw() ],
+ argspec => 1,
+ short => "kill processes on all nodes",
+ help => <<END,
+kill dbname [options]
+dbname database name
+
+Send SIGKILL to processes on all nodes and wait for them to exit.
+END
+ },
+ {
+ name => "killnode",
+ optspec => [ qw(local) ],
+ argspec => 2,
+ short => "kill process on one node",
+ help => <<END,
+killnode dbname nodeid [options]
+dbname database name
+nodeid node number
+--local node must be local to this server
+
+Send SIGKILL to the process on the node and wait for it to exit.
+
+Used internally by db "kill" command.
+END
+ },
+ {
+ name => "statnode",
+ optspec => [ qw(local) ],
+ argspec => 2,
+ short => "get node run status (internal)",
+ help => <<END,
+statnode dbname nodeid [options]
+dbname database name
+nodeid node number
+--local node must be local to this server
+
+Get node run status (up/down) as a process. Used internally
+and may not produce any output in ndbnet command.
+END
+ },
+ {
+ name => "list",
+ optspec => [ qw(quick short) ],
+ argspec => sub { 1 },
+ short => "list databases",
+ help => <<END,
+list [dbname] [options]
+dbname database name, default is to list all
+--quick only output config, do not query status
+--short do list nodes
+
+List databases and nodes. Internally returns a data structure
+of process and mgmt server status values for each node. Externally
+(in ndbnet command) this is formatted as a listing.
+END
+ },
+ {
+ name => "writenode",
+ optspec => [ qw(wait=i local) ],
+ argspec => 3,
+ short => "write line of text to the process on a node",
+ help => <<END,
+writenode dbname nodeid "some text"
+dbname database name
+nodeid node number
+"some text" arbitrary text (quote if spaces)
+--wait n wait n seconds for any response
+--local node must be local to this server
+
+Write the text and a newline to the standard input of the process
+running on the node. If wait > 0 is specified, prints whatever
+the process wrote to stdout/stderr during that time.
+
+Used internally by "start" and other commands.
+END
+ },
+];
+
+# aliases
+# name alias
+# value expansion
+
+$aliastab = [
+ {
+ name => "h",
+ value => "help",
+ },
+ {
+ name => "q",
+ value => "quit",
+ },
+ {
+ name => "EOF",
+ value => "quit",
+ },
+ {
+ name => "startserver",
+ value => "server start",
+ },
+ {
+ name => "ss",
+ value => "server start",
+ },
+ {
+ name => "restartserver",
+ value => "server restart",
+ },
+ {
+ name => "rss",
+ value => "server restart",
+ },
+ {
+ name => "stopserver",
+ value => "server stop",
+ },
+ {
+ name => "pingserver",
+ value => "server ping",
+ },
+ {
+ name => "ps",
+ value => "server ping",
+ },
+ {
+ name => "l",
+ value => "list",
+ },
+];
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Config.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Config.pm
new file mode 100644
index 00000000000..4c5db3cd3f5
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Config.pm
@@ -0,0 +1,235 @@
+package NDB::Net::Config;
+
+use strict;
+use Carp;
+use Symbol;
+use Socket;
+use Errno;
+use XML::Parser;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::Config->attributes(
+ file => sub { /^\S+$/ },
+ loadtime => sub { /^\d+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $netcfg = $class->SUPER::new(%attr);
+ $netcfg->setfile($attr{file})
+ or $log->put, return undef;
+ return $netcfg;
+}
+
+sub desc {
+ my $netcfg = shift;
+ return $netcfg->getfile;
+}
+
+use vars qw(@context);
+
+sub handle_start {
+ my($parser, $tag, %attr) = @_;
+ my $p = $context[-1];
+ my $q = {};
+ $p->{$tag} ||= [];
+ push(@{$p->{$tag}}, $q);
+ for my $k (keys %attr) {
+ $q->{$k} = $attr{$k};
+ }
+ push(@context, $q);
+ return 1;
+}
+
+sub handle_end {
+ my($parser, $tag, %attr) = @_;
+ pop(@context);
+ return 1;
+}
+
+sub load {
+ my $netcfg = shift;
+ my $file = $netcfg->getfile;
+ my @s;
+ while (1) {
+ if (@s = stat($file)) {
+ last;
+ }
+ $log->put("$file: stat failed: $!");
+ if (! $!{ESTALE}) {
+ return undef;
+ }
+ $log->put("(retry)")->info;
+ sleep 1;
+ }
+ if ($s[9] <= $netcfg->getloadtime(0)) {
+ return 1;
+ }
+ my $fh = gensym();
+ if (! open($fh, "<$file")) {
+ $log->put("$file: open for read failed: $!");
+ return undef;
+ }
+ my $text = "";
+ my $line;
+ while (defined($line = <$fh>)) {
+ $text .= $line;
+ }
+ close($fh);
+ my $parser = XML::Parser->new(
+ ParseParamEnt => 1,
+ Handlers => {
+ Start => \&handle_start,
+ End => \&handle_end,
+ },
+ );
+ delete $netcfg->{config};
+ local @context = ($netcfg);
+ $parser->parse($text);
+ $netcfg->{text} = $text;
+ $netcfg->{config} = $netcfg->{config}[0];
+ $netcfg->setloadtime(time)
+ or $log->push, return undef;
+ NDB::Net::Server->deleteall;
+ NDB::Net::Database->deleteall;
+ NDB::Net::Node->deleteall;
+ return 1;
+}
+
+sub getservers {
+ my $netcfg = shift;
+ @_ == 0 or confess 0+@_;
+ my $servers = [];
+ my $slist = $netcfg->{config}{server} || [];
+ for my $s (@$slist) {
+ my $server;
+ $server = NDB::Net::ServerINET->get($s->{id});
+ if (! $server) {
+ $server = NDB::Net::ServerINET->new(%$s);
+ if (! $server) {
+ $log->push($netcfg)->warn;
+ next;
+ }
+ }
+ push(@$servers, $server);
+ }
+ return $servers;
+}
+
+sub getdatabases {
+ my $netcfg = shift;
+ @_ == 0 or confess 0+@_;
+ my $databases = [];
+ my $dlist = $netcfg->{config}{database} || [];
+ for my $d (@$dlist) {
+ if ($d->{isproto} eq "y") {
+ next;
+ }
+ if ($d->{name} !~ /^\w(\w|-)*$/) {
+ $log->put("$d->{name}: invalid db name")->push($netcfg)->warn;
+ next;
+ }
+ my $db = $netcfg->getdatabase($d->{name});
+ if (! $db) {
+ $log->push->warn;
+ next;
+ }
+ push(@$databases, $db);
+ }
+ return $databases;
+}
+
+sub getdatabase {
+ my $netcfg = shift;
+ @_ == 1 or confess 0+@_;
+ my($name) = @_;
+ $netcfg->getservers or return undef; # cache them
+ my $default = $netcfg->{config}{default}[0] || {};
+ my $db;
+ my $dlist = $netcfg->{config}{database} || [];
+ my $nlist;
+ for my $d (@$dlist) {
+ ($d->{name} ne $name) && next;
+ if ($d->{isproto} eq "y") {
+ next;
+ }
+ my %attr = (%$default, %$d);
+ $db = NDB::Net::Database->new(%attr);
+ if (! $db) {
+ $log->push($netcfg);
+ return undef;
+ }
+ if ($d->{proto}) {
+ if ($d->{isproto} eq "y") {
+ $log->put("$name: prototypes cannot be recursive");
+ return undef;
+ }
+ for my $d2 (@$dlist) {
+ ($d2->{name} ne $d->{proto}) && next;
+ if ($d2->{isproto} ne "y") {
+ $log->put("$name: $d2->{name} is not a prototype");
+ return undef;
+ }
+ if (! $d->{node}) {
+ $d->{node} = $d2->{node};
+ }
+ last;
+ }
+ }
+ $nlist = $d->{node} || [];
+ last;
+ }
+ if (! $db) {
+ $log->put("$name: no such db")->push($netcfg);
+ return undef;
+ }
+ if (! @$nlist) {
+ $log->put("$name: empty node list")->push($netcfg);
+ return undef;
+ }
+ for my $n (@$nlist) {
+ my $node;
+ try: {
+ my $server = NDB::Net::Server->get($n->{server})
+ or last try;
+ my %attr = (%$n, db => $db, server => $server);
+ my $type = $attr{type};
+ if ($type eq 'db') {
+ $node = NDB::Net::NodeDb->new(%attr)
+ or last try;
+ }
+ if ($type eq 'mgmt') {
+ $node = NDB::Net::NodeMgmt->new(%attr)
+ or last try;
+ }
+ if ($type eq 'api') {
+ $node = NDB::Net::NodeApi->new(%attr)
+ or last try;
+ }
+ $log->put("bad node type '$type'");
+ }
+ if (! $node) {
+ $log->push($netcfg);
+ $db->delete;
+ return undef;
+ }
+ }
+ return $db;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Database.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Database.pm
new file mode 100644
index 00000000000..7ea15be0650
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Database.pm
@@ -0,0 +1,321 @@
+package NDB::Net::Database;
+
+use strict;
+use Carp;
+use Symbol;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+my %dbcache = ();
+
+NDB::Net::Database->attributes(
+ name => sub { s/^\s+|\s+$//g; /^\S+$/ && ! m!/! },
+ comment => sub { defined },
+ version => sub { /^\d+(\.\d+)*$/ },
+ base => sub { $^O eq 'MSWin32' || m!^/\S+$! },
+ home => sub { $^O eq 'MSWin32' || m!^/\S+$! },
+ nodeport => sub { $_ > 0 },
+);
+
+sub desc {
+ my $db = shift;
+ return $db->getname;
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $db = $class->SUPER::new(%attr);
+ $db->setname($attr{name})
+ or $log->push, return undef;
+ if ($dbcache{$db->getname}) {
+ $log->put("duplicate db")->push($db);
+ return undef;
+ }
+ $db->setcomment($attr{comment});
+ $db->setversion($attr{version})
+ or $log->push, return undef;
+ if (defined($attr{base})) {
+ $db->setbase($attr{base})
+ or $log->push, return undef;
+ }
+ if (defined($attr{home})) {
+ if ($^O ne 'MSWin32' && $attr{home} !~ m!^/! && $db->hasbase) {
+ $attr{home} = $db->getbase . "/$attr{home}";
+ }
+ $db->sethome($attr{home})
+ or $log->push, return undef;
+ }
+ if (defined($attr{nodeport})) {
+ $db->setnodeport($attr{nodeport})
+ or $log->push, return undef;
+ }
+ if ($^O eq 'MSWin32' && ! $db->hasnodeport) {
+ $log->put("nodeport required on windows")->push($db), return undef;
+ }
+ $db->{nodehash} = {};
+ $dbcache{$db->getname} = $db;
+ return $db;
+}
+
+sub delete {
+ my $db = shift;
+ my $nodelist = $db->getnodelist('all');
+ for my $node (@$nodelist) {
+ $node->delete;
+ }
+ delete $dbcache{$db->getname};
+}
+
+sub deleteall {
+ my $class = shift;
+ for my $name (sort keys %dbcache) {
+ my $db = $dbcache{$name};
+ $db->delete;
+ }
+}
+
+# assume numerical dot separated version numbers like 1.1.2
+sub cmpversion {
+ my $db = shift;
+ my $version = shift;
+ my @x = split(/\./, $db->getversion);
+ my @y = split(/\./, $version);
+ while (@x || @y) {
+ return -1 if $x[0] < $y[0];
+ return +1 if $x[0] > $y[0];
+ shift(@x);
+ shift(@y);
+ }
+ return 0;
+}
+
+# nodes
+
+sub addnode {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($node) = @_;
+ unless (ref($node) && $node->isa('NDB::Net::Node')) {
+ confess 'oops';
+ }
+ my $id = $node->getid;
+ if ($db->{nodehash}{$id}) {
+ $log->put("$id: duplicate node id")->push($db);
+ return undef;
+ }
+ $db->{nodehash}{$id} = $node;
+ return 1;
+}
+
+sub getnode {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($id) = @_;
+ $id += 0;
+ my $node = $db->{nodehash}{$id};
+ if (! $node) {
+ $log->put("$id: no such node id")->push($db);
+ return undef;
+ }
+ return $node;
+}
+
+sub getnodelist {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($type) = @_;
+ $type =~ /^(all|mgmt|db|api)$/ or confess 'oops';
+ my @nodes = ();
+ for my $id (sort { $a <=> $b } keys %{$db->{nodehash}}) {
+ my $node = $db->{nodehash}{$id};
+ if ($type eq 'all' or $type eq $node->gettype) {
+ push(@nodes, $node);
+ }
+ }
+ return \@nodes;
+}
+
+# start /stop
+
+sub start {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ if ($opts->{stop} || $opts->{kill}) {
+ my $method = $opts->{stop} ? "stop" : "kill";
+ my %opts = ();
+ $db->$method(\%opts)
+ or $log->push, return undef;
+ }
+ $log->put("start")->push($db)->info;
+ my $nodesmgmt = $db->getnodelist('mgmt');
+ my $nodesdb = $db->getnodelist('db');
+ my $nodesapi = $db->getnodelist('api');
+ my $ret;
+ try: {
+ my %startopts = ();
+ for my $k (qw(local init_rm nostart config old home clean proxy)) {
+ $startopts{$k} = $opts->{$k} if defined($opts->{$k});
+ }
+ my %writeopts = ();
+ for my $k (qw(local)) {
+ $writeopts{$k} = $opts->{$k} if defined($opts->{$k});
+ }
+ if ($db->cmpversion("1.0") > 0) {
+ for my $node (@$nodesmgmt) {
+ $node->start(\%startopts) or last try;
+ }
+ for my $node (@$nodesdb) {
+ $node->start(\%startopts) or last try;
+ }
+ if (! $opts->{config}) {
+ for my $node (@$nodesmgmt) { # probably redundant
+ $node->write(\%writeopts, "all start") or last try;
+ last;
+ }
+ }
+ }
+ else {
+ for my $node (@$nodesdb) {
+ $node->start(\%startopts) or last try;
+ }
+ if (! $opts->{config}) {
+ for my $node (@$nodesdb) { # probably redundant
+ $node->write(\%writeopts, "start") or last try;
+ }
+ }
+ }
+ for my $node (@$nodesapi) {
+ my %apiopts = %startopts;
+ if ($node->getruntype eq 'manual') {
+ $apiopts{config} = 1;
+ }
+ $node->start(\%apiopts) or last try;
+ }
+ $ret = 1;
+ }
+ if (! $ret) {
+ $log->push("start failed")->push($db);
+ return undef;
+ }
+ my $msg = ! $opts->{config} ? "start done" : "config created";
+ $log->put($msg)->push($db)->user;
+ return 1;
+}
+
+sub stop {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("stop")->push($db)->info;
+ my $nodesmgmt = $db->getnodelist('mgmt');
+ my $nodesdb = $db->getnodelist('db');
+ my $nodesapi = $db->getnodelist('api');
+ my $ret;
+ try: {
+ for my $node (@$nodesapi) {
+ $node->stop($opts) or last try;
+ }
+ if ($db->cmpversion("1.0") > 0) {
+ for my $node (@$nodesmgmt) {
+ $node->write($opts, "all stop") or last try;
+ last;
+ }
+ for my $node (@$nodesdb) {
+ $node->stop($opts) or last try;
+ }
+ for my $node (@$nodesmgmt) {
+ $node->stop($opts) or last try;
+ }
+ }
+ else {
+ for my $node (@$nodesdb) {
+ $node->write($opts, "stop") or last try;
+ }
+ for my $node (@$nodesdb) {
+ $node->stop($opts) or last try;
+ }
+ }
+ $ret = 1;
+ }
+ if (! $ret) {
+ $log->push("stop failed")->push($db);
+ return undef;
+ }
+ $log->put("stop done")->push($db)->user;
+ return 1;
+}
+
+sub kill {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("kill")->push($db)->info;
+ my $nodesmgmt = $db->getnodelist('mgmt');
+ my $nodesdb = $db->getnodelist('db');
+ my $nodesapi = $db->getnodelist('api');
+ my $ret = 1;
+ try: {
+ for my $node (@$nodesapi) {
+ $node->kill($opts) || ($ret = undef);
+ }
+ for my $node (@$nodesdb) {
+ $node->kill($opts) || ($ret = undef);
+ }
+ for my $node (@$nodesmgmt) {
+ $node->kill($opts) || ($ret = undef);
+ }
+ }
+ if (! $ret) {
+ $log->push("kill failed")->push($db);
+ return undef;
+ }
+ $log->put("kill done")->push($db)->user;
+ return 1;
+}
+
+sub list {
+ my $db = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ my $dbsts = {};
+ $dbsts->{comment} = $db->getcomment("");
+ $dbsts->{home} = $db->gethome;
+ $log->put("status")->push($db)->info;
+ my $mgmsts;
+ for my $node (@{$db->getnodelist('mgmt')}) {
+ $mgmsts = $node->get_status or
+ $log->push->error;
+ last;
+ }
+ $mgmsts ||= {};
+ for my $node (@{$db->getnodelist('all')}) {
+ my $id = $node->getid;
+ my $nodests = $dbsts->{node}{$id} ||= {};
+ my $stat = $node->stat($opts) or
+ $log->push->error;
+ $nodests->{id} = $id;
+ $nodests->{type} = $node->gettype;
+ $nodests->{comment} = $node->getcomment("");
+ $nodests->{host} = $node->getserver->gethost;
+ $nodests->{run} = $stat || "error";
+ $nodests->{status} = $mgmsts->{node}{$id};
+ }
+ return $dbsts;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Env.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Env.pm
new file mode 100644
index 00000000000..d79e72f2bb3
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Env.pm
@@ -0,0 +1,94 @@
+package NDB::Net::Env;
+
+use strict;
+use File::Spec;
+use Carp;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# environment variables
+#
+# NDB_TOP source dir or installation dir
+# NDB_BASE base dir not tied to any release or database
+# NDB_NETCFG ndbnet config file, default $NDB_BASE/etc/ndbnet.xml
+#
+# ndbnet explicitly unsets NDB_TOP and NDB_HOME because they are
+# specific to each database or database node
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::Env->attributes(
+ base => sub { /^\S+$/ },
+ netcfg => sub { /^\S+$/ },
+ hostname => sub { /^\S+$/ },
+);
+
+my $instance;
+
+sub desc {
+ my $netenv = shift;
+ return "net environment";;
+}
+
+sub instance {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ if ($instance) {
+ return $instance;
+ }
+ for my $var (qw(NDB_TOP NDB_HOME)) {
+ my $top = delete $ENV{$var};
+ if (defined($top)) {
+ if ($^O ne 'MSWin32') {
+ $ENV{PATH} =~ s!(^|:)$top/bin($|:)!$1$2!g;
+ $ENV{LD_LIBRARY_PATH} =~ s!(^|:)$top/lib($|:)!$1$2!g;
+ $ENV{PERL5LIB} =~ s!(^|:)$top/lib/perl5($|:)!$1$2!g;
+ }
+ }
+ }
+ my $netenv = $class->SUPER::new(%attr);
+ for my $base ($attr{base}, $ENV{NDB_BASE}) {
+ if (defined($base)) {
+ $netenv->setbase($base)
+ or $log->push, return undef;
+ }
+ }
+ for my $netcfg ($attr{netcfg}, $ENV{NDB_NETCFG}) {
+ if (defined($netcfg)) {
+ $netenv->setnetcfg($netcfg)
+ or $log->push, return undef;
+ }
+ }
+ if ($netenv->hasbase && ! $netenv->hasnetcfg) {
+ $netenv->setnetcfg(File::Spec->catfile($netenv->getbase, "etc", "ndbnet.xml"))
+ or $log->push, return undef;
+ }
+ my $uname;
+ if ($^O ne 'MSWin32') {
+ chomp($uname = `uname -n`);
+ } else {
+ chomp($uname = `hostname`);
+ }
+ my($hostname) = gethostbyname($uname);
+ if (! defined($hostname)) {
+ $uname =~ s!\..*$!!;
+ ($hostname) = gethostbyname($uname);
+ }
+ $netenv->sethostname($hostname)
+ or $log->push, return undef;
+ $instance = $netenv;
+ return $instance;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Node.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Node.pm
new file mode 100644
index 00000000000..f41bf51168d
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Node.pm
@@ -0,0 +1,747 @@
+package NDB::Net::Node;
+
+use strict;
+use Carp;
+use Symbol;
+use Socket;
+use IPC::Open3;
+use POSIX();
+use Errno;
+use File::Spec;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+my %nodecache = ();
+
+NDB::Net::Node->attributes(
+ db => sub { ref && $_->isa('NDB::Net::Database') },
+ comment => sub { defined },
+ id => sub { s/^\s+|\s+$//g; s/^0+(\d+)$/$1/; /^\d+$/ && $_ > 0 },
+ type => sub { s/^\s+|\s+$//g; /^(mgmt|db|api)$/ },
+ server => sub { ref && $_->isa('NDB::Net::Server') },
+ base => sub { File::Spec->file_name_is_absolute($_) },
+ home => sub { File::Spec->file_name_is_absolute($_) },
+ state => sub { /^(new|run|stop)$/ },
+ run => sub { defined },
+ runenv => sub { defined },
+ runtype => sub { m!(auto|once|manual)$! },
+ lockpid => sub { $_ != 0 },
+ iow => sub { ref && $_->isa('NDB::Util::IO') },
+ ior => sub { ref && $_->isa('NDB::Util::IO') },
+ pid => sub { $_ > 1 },
+ event => sub { ref && $_->isa('NDB::Util::Event') },
+);
+
+sub desc {
+ my $node = shift;
+ my $dbname = $node->getdb->getname;
+ my $id = $node->getid;
+ my $type = $node->gettype;
+ return "$dbname.$id-$type";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $node = $class->SUPER::new(%attr);
+ $node->setdb($attr{db})
+ or $log->push, return undef;
+ $node->setid($attr{id})
+ or $log->push, return undef;
+ if ($nodecache{$node->getdb->getname,$node->getid}) {
+ $log->put("duplicate node")->push($node);
+ return undef;
+ }
+ $node->setcomment($attr{comment});
+ $node->settype($attr{type})
+ or $log->push, return undef;
+ if ($node->getdb->cmpversion("1.0") <= 0 && $node->gettype eq 'mgmt') {
+ $log->put("no mgmt nodes in db version <= 1.0")->push($node);
+ return undef;
+ }
+ $node->setserver($attr{server})
+ or $log->push, return undef;
+ for my $base ($attr{base}, $node->getdb->getbase(undef)) {
+ if (defined($base)) {
+ $node->setbase($base)
+ or $log->push, return undef;
+ }
+ }
+ for my $home ($attr{home}, $node->getdb->gethome(undef)) {
+ if (defined($home)) {
+ if ($^O ne 'MSWin32' && $home !~ m!^/! && $node->hasbase) {
+ $home = $node->getbase . "/$home";
+ }
+ $node->sethome($home)
+ or $log->push, return undef;
+ }
+ }
+ if (! $node->hashome) {
+ $log->put("home not defined")->push($node);
+ return undef;
+ }
+ $node->setstate('new')
+ or $log->push, return undef;
+ if (defined($attr{run})) {
+ $node->setrun($attr{run})
+ or $log->push, return undef;
+ }
+ if (defined($attr{runenv})) {
+ $node->setrunenv($attr{runenv})
+ or $log->push, return undef;
+ }
+ if (defined($attr{runtype})) {
+ $node->setruntype($attr{runtype})
+ or $log->push, return undef;
+ }
+ if (! $node->hasruntype) {
+ my $runtype = "manual";
+ $runtype = "once"
+ if $node->gettype =~ /^(mgmt|db)$/ || $node->hasrun;
+ $node->setruntype($runtype)
+ or $log->push, return undef;
+ }
+ if (! $node->getdb->addnode($node)) {
+ $log->push;
+ return undef;
+ }
+ $nodecache{$node->getdb->getname,$node->getid} = $node;
+ return $node;
+}
+
+sub delete {
+ my $node = shift;
+ delete $nodecache{$node->getdb->getname,$node->getid} or
+ confess 'oops';
+}
+
+sub deleteall {
+ my $class = shift;
+ for my $k (sort keys %nodecache) {
+ my $node = $nodecache{$k};
+ $node->delete;
+ }
+}
+
+# node startup
+
+sub getconfdir {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $netenv = NDB::Net::Env->instance;
+ my $name = File::Spec->catfile($netenv->getbase, "etc");
+ my $dir = NDB::Util::Dir->new(path => $name);
+ return $dir;
+}
+
+sub getdbdir {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $netenv = NDB::Net::Env->instance;
+ my $name = File::Spec->catfile($netenv->getbase, "db", $node->getdb->getname);
+ my $dir = NDB::Util::Dir->new(path => $name);
+ return $dir;
+}
+
+sub getnodedir {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("%s-%s", $node->getid, $node->gettype);
+ my $dir = $node->getdbdir->getdir($name);
+ return $dir;
+}
+
+sub getrundir {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("run");
+ my $dir = $node->getdbdir->getdir($name);
+ return $dir;
+}
+
+sub getlogdir {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("log");
+ my $dir = $node->getdbdir->getdir($name);
+ return $dir;
+}
+
+sub getlock {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("%s-%s.pid", $node->getid, $node->gettype);
+ my $lock = $node->getrundir->getfile($name)->getlock;
+ return $lock;
+}
+
+sub getsocketfile {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("%s-%s.socket", $node->getid, $node->gettype);
+ my $file = $node->getrundir->getfile($name);
+ return $file;
+}
+
+sub getlogfile {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("%s-%s.log", $node->getid, $node->gettype);
+ my $file = $node->getlogdir->getfile($name);
+ return $file;
+}
+
+sub getshellfile {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("run.sh");
+ my $file = $node->getnodedir->getfile($name);
+ return $file;
+}
+
+sub getlocalcfg {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = "Ndb.cfg";
+ my $file = $node->getnodedir->getfile($name);
+ return $file;
+}
+
+sub writelocalcfg {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $db = $node->getdb;
+ my $file = $node->getlocalcfg;
+ $file->mkdir or $log->push, return undef;
+ if ($db->cmpversion("1.0") <= 0) {
+ my $section = "";
+ my $edit = sub {
+ chomp;
+ if (/^\s*\[\s*(\S+)\s*\]/) {
+ $section = uc($1);
+ }
+ if ($section eq 'OWN_HOST') {
+ if (/^\s*ThisHostId\b/i) {
+ $_ = "ThisHostId " . $node->getid;
+ }
+ }
+ if ($section eq 'CM') {
+ if (/^\s*ThisNodeId\b/i) {
+ $_ = "ThisNodeId " . $node->getid;
+ }
+ }
+ if (0 and $section eq 'PROCESS_ID') {
+ if (/^\s*Host(\d+)\s+(\S+)(.*)/) {
+ my $id2 = $1;
+ my $host2 = $2;
+ my $rest2 = $3;
+ my $node2 = $db->getnode($id2)
+ or $log->push, return undef;
+ $_ = "Host$id2 ";
+ $_ .= $node2->getserver->getcanon;
+ $_ .= " $rest2";
+ }
+ }
+ $_ .= "\n";
+ return 1;
+ };
+ $node->getinifile->copyedit($file, $edit)
+ or $log->push, return undef;
+ }
+ else {
+ my @text = ();
+ push(@text, sprintf("OwnProcessId %s", $node->getid));
+ my $nodesmgmt = $db->getnodelist('mgmt');
+ for my $mnode (@$nodesmgmt) {
+ my $host = $mnode->getserver->getcanon;
+ my $port = $mnode->getport;
+ push(@text, "$host $port");
+ }
+ $file->putlines(\@text) or $log->push, return undef;
+ }
+ return 1;
+}
+
+sub getinifile {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("%s.ini", $node->getdb->getname);
+ my $file = $node->getconfdir->getfile($name);
+ return $file;
+}
+
+sub getbincfg {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = sprintf("config.bin");
+ my $file = $node->getnodedir->getfile($name);
+ return $file;
+}
+
+sub getenvdefs {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my $opts = shift;
+ my $home = $opts->{home} || $node->gethome;
+ my $netenv = NDB::Net::Env->instance;
+ if (! File::Spec->file_name_is_absolute($home)) {
+ $netenv->hasbase
+ or $log->put("no base and home=$home not absolute"), return undef;
+ $home = File::Spec->catfile($netenv->getbase, $home);
+ }
+ (-d $home)
+ or $log->put("$home: no such directory"), return undef;
+ my $defs;
+ if ($^O ne 'MSWin32') {
+ $defs = <<END;
+# @{[ $node->desc ]} @{[ $node->getcomment("") ]}
+# @{[ $node->getserver->desc ]} @{[ $node->getserver->getcanon ]}
+#
+debugger=\$1
+#
+NDB_TOP=$home
+export NDB_TOP
+PATH=\$NDB_TOP/bin:\$PATH
+export PATH
+LD_LIBRARY_PATH=\$NDB_TOP/lib:\$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+PERL5LIB=\$NDB_TOP/lib/perl5:\$PERL5LIB
+export PERL5LIB
+NDB_NODEID=@{[ $node->getid ]}
+export NDB_NODEID
+NDB_NODETYPE=@{[ $node->gettype ]}
+export NDB_NODETYPE
+ulimit -Sc unlimited
+END
+ if ($node->hasrunenv) {
+ $defs .= <<END;
+#
+cd @{[ $node->getnodedir->getpath ]} || exit 1
+@{[ $node->getrunenv ]}
+END
+ }
+ $defs .= <<END;
+#
+unset NDB_HOME # current NdbConfig.c would look here
+#
+END
+ } else {
+ $defs = <<END;
+rem @{[ $node->desc ]} @{[ $node->getcomment("") ]}
+rem @{[ $node->getserver->desc ]} @{[ $node->getserver->getcanon ]}
+rem
+set NDB_TOP=$home
+set PATH=%NDB_TOP%\\bin;%PATH%
+set PERL5LIB=%NDB_TOP%\\lib\\perl5;%PERL5LIB%
+set NDB_NODEID=@{[ $node->getid ]}
+set NDB_NODETYPE=@{[ $node->gettype ]}
+END
+ if ($node->hasrunenv) {
+ $defs .= <<END;
+rem
+@{[ $node->getrunenv ]}
+END
+ }
+ $defs .= <<END;
+rem
+rem current NdbConfig.c would look here
+set NDB_HOME=
+rem
+END
+ }
+ chomp($defs);
+ return $defs;
+}
+
+sub startlocal {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("start local")->push($node)->info;
+ my $lock = $node->getlock;
+ $lock->mkdir or $log->push, return undef;
+ anon: {
+ my $ret = $lock->test;
+ defined($ret) or $log->push, return undef;
+ if ($ret) {
+ $log->put("already running under serverpid=%s",
+ $lock->getpid)->push($node)->user;
+ return 1;
+ }
+ $lock->set or $log->push, return undef;
+ }
+ if ($opts->{clean}) {
+ $node->getnodedir->rmdir(1);
+ $node->getlogfile->unlink;
+ }
+ if (! $opts->{old}) {
+ $node->writelocalcfg or $log->push, return undef;
+ $node->handleprepare($opts) or $log->push, return undef;
+ }
+ anon: {
+ $lock->close;
+ if ($opts->{config}) {
+ return 1;
+ }
+ my $file = $node->getlogfile;
+ $file->mkdir or $log->push, return undef;
+ my $pid = fork();
+ defined($pid) or $log->put("fork failed: $!"), return undef;
+ if ($pid) {
+ exit(0);
+ }
+ $lock->set or $log->push->fatal;
+ $node->setlockpid($$) or $log->push->fatal;
+ if ($^O ne 'MSWin32') {
+ POSIX::setsid() or $log->put("setsid failed: $!")->fatal;
+ }
+ $log->setfile($file->getpath) or $log->push->fatal;
+ }
+ my $socket;
+ anon: {
+ my $file = $node->getsocketfile;
+ $file->mkdir or $log->push($node)->fatal;
+ unlink($file->getpath);
+ if ($^O ne 'MSWin32') {
+ $socket = NDB::Util::SocketUNIX->new
+ or $log->push($node)->fatal;
+ } else {
+ $socket = NDB::Util::SocketINET->new
+ or $log->push($node)->fatal;
+ }
+ $socket->setopt(SOL_SOCKET, SO_REUSEADDR, 1)
+ or $log->push($node)->fatal;
+ if ($^O ne 'MSWin32') {
+ $socket->bind($file->getpath)
+ or $log->push($node)->fatal;
+ } else {
+ $socket->bind($node->getdb->getnodeport + $node->getid)
+ or $log->push($node)->fatal;
+ }
+ $socket->listen
+ or $log->push($node)->fatal;
+ }
+ START: {
+ my $w = gensym();
+ my $r = gensym();
+ my @arg = ('/bin/sh', $node->getshellfile->getpath);
+ my $pid = open3($w, $r, undef, @arg);
+ $node->setiow(NDB::Util::IO->new(fh => $w))
+ or $log->push->fatal;
+ $node->setior(NDB::Util::IO->new(fh => $r))
+ or $log->push->fatal;
+ $node->setpid($pid)
+ or $log->push->fatal;
+ }
+ $node->setstate('run')
+ or $log->push($node)->fatal;
+ $log->put("started host=%s pid=%s",
+ $node->getserver->gethost, $node->getpid)->push($node)->user;
+ $log->push("started")->push($node)->putvalue(1)->user;
+ $log->detachuser;
+ NDB::Net::Client->deleteall;
+ my $event = NDB::Util::Event->new;
+ $event->set($socket, 'r');
+ $event->set($node->getior, 'r');
+ loop: {
+ try: {
+ my $n = $event->poll(10);
+ if (! defined($n)) {
+ $log->push->error;
+ sleep 1;
+ last try;
+ }
+ if (! $n) {
+ $log->push->debug;
+ last try;
+ }
+ if ($node->hasior && $event->test($node->getior, 'r')) {
+ my $data = $node->getior->read;
+ if (! defined($data)) {
+ $log->push->fatal;
+ }
+ if (length($data) > 0) {
+ $node->handleoutput($opts, $data);
+ }
+ if ($node->getior->getreadend) {
+ $log->put("input closed")->warn;
+ $event->clear($node->getior, 'r');
+ $node->getior->close;
+ $node->delior;
+ $node->handleeof($opts);
+ last loop;
+ }
+ }
+ if (! $event->test($socket, 'r')) {
+ last try;
+ }
+ my $csocket = $socket->accept(10);
+ if (! defined($csocket)) {
+ $log->push->error;
+ last try;
+ }
+ if (! $csocket) {
+ $log->push->warn;
+ last try;
+ }
+ my $client = NDB::Net::Client->new(
+ socket => $csocket,
+ serversocket => $socket,
+ serverlock => $lock,
+ event => $event,
+ context => $node,
+ );
+ $client or $log->push->fatal;
+ }
+ NDB::Net::Client->processall;
+ redo loop;
+ }
+ if ($node->getruntype eq "auto") {
+ if ($node->getstate eq "run") {
+ $log->put("restart in 5 seconds...")->info;
+ sleep 5;
+ goto START;
+ }
+ $log->put("stopping, skip restart")->info;
+ }
+ $lock->close;
+ $node->getsocketfile->unlink;
+ while (wait() != -1) {}
+ $log->put("exit")->push->info;
+ exit(0);
+}
+
+# handlers can be overridden in subclass
+
+sub handleprepare { confess 'oops'; }
+
+sub handleoutput {
+ my $node = shift;
+ @_ == 2 or confess 0+@_;
+ my($opts, $data) = @_;
+ $data =~ s/\015//g;
+ $data = $node->{savedata} . $data;
+ while ((my $i = index($data, "\n")) >= 0) {
+ my $line = substr($data, 0, $i);
+ $data = substr($data, $i+1);
+ $log->put($line)->info;
+ if ($opts->{user} && $line !~ /^\s*$/) {
+ $log->put($line)->user;
+ }
+ }
+ $node->{savedata} = $data;
+ if (1 && length $node->{savedata}) { # XXX partial line
+ my $line = $node->{savedata};
+ $log->put($line)->info;
+ if ($opts->{user} && $line !~ /^\s*$/) {
+ $log->put($line)->user;
+ }
+ $node->{savedata} = "";
+ }
+}
+
+sub handleeof {
+}
+
+# command subs can be overridden by subclass
+
+sub waitforexit {
+ my $node = shift;
+ my $lock = $node->getlock;
+ my $lockpid = $node->getlockpid;
+ my $n1 = 0;
+ my $n2 = 10;
+ while (1) {
+ my $ret = $lock->test;
+ defined($ret) or $log->push, return undef;
+ if (! $ret) {
+ $log->put("exit done")->push($node)->user;
+ last;
+ }
+ if ($lockpid != $lock->getpid) {
+ $log->put("restarted: lock pid changed %s->%s",
+ $lockpid, $lock->getpid)->push($node);
+ return undef;
+ }
+ if (++$n1 >= $n2) {
+ $n2 *= 2;
+ $log->put("wait for exit")->push($node)->user;
+ }
+ select(undef, undef, undef, 0.1);
+ }
+ return 1;
+}
+
+sub cmd_stopnode_bg {
+ my($node, $cmd) = @_;
+ return $node->waitforexit;
+}
+
+sub cmd_killnode_fg {
+ my($node, $cmd) = @_;
+ my $pid = $node->getpid;
+ $log->put("kill -9 $pid")->push($node)->user;
+ kill(9, $pid);
+ $node->setstate('stop')
+ or $log->push($node), return undef;
+ return 1;
+}
+
+sub cmd_killnode_bg {
+ my($node, $cmd) = @_;
+ return $node->waitforexit;
+}
+
+sub cmd_statnode_bg {
+ my($node, $cmd) = @_;
+ return "up";
+}
+
+sub cmd_writenode_fg {
+ my($node, $cmd) = @_;
+ my $text = $cmd->getarg(2);
+ while(chomp($text)) {};
+ $log->put("write: $text")->push($node)->user;
+ $node->getiow->write("$text\n");
+ my $output = "";
+ if ((my $num = $cmd->getopt("wait")) > 0) {
+ my $lim = time + $num;
+ $node->getior->settimeout(1);
+ loop: {
+ my $data = $node->getior->read;
+ if (length($data) > 0) {
+ $node->handleoutput({user => 1}, $data);
+ $output .= $data;
+ }
+ redo loop if time < $lim;
+ }
+ $node->getior->settimeout(0);
+ }
+ return { output => $output };
+}
+
+# commands
+
+sub doremote {
+ my $node = shift;
+ my($cmdname, $opts, @args) = @_;
+ my $server = $node->getserver;
+ $log->put("$cmdname remote")->push($server)->push($node)->info;
+ my $argv = [
+ $cmdname, q(--local),
+ $opts, $node->getdb->getname, $node->getid, @args ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $server->request($cmd)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub dolocal {
+ my $node = shift;
+ my($cmdname, $opts, @args) = @_;
+ $log->put("$cmdname local")->push($node)->info;
+ if (! $node->getserver->islocal) {
+ $log->put("not local")->push($node->getserver)->push($node);
+ return undef;
+ }
+ if ($cmdname eq "startnode") {
+ return $node->startlocal($opts);
+ }
+ my $lock = $node->getlock;
+ anon: {
+ my $ret = $lock->test;
+ defined($ret) or $log->push, return undef;
+ if (! $ret) {
+ if ($cmdname eq "statnode") {
+ return "down";
+ }
+ $log->put("not running")->push($node)->user;
+ return $cmdname eq "writenode" ? undef : 1;
+ }
+ }
+ my $server;
+ anon: {
+ my $path = $node->getsocketfile->getpath;
+ if (! -e $path) {
+ $log->put("$path: no socket")->push($node);
+ return undef;
+ }
+ if ($^O ne 'MSWin32') {
+ $server = NDB::Net::ServerUNIX->new(id => 0, path => $path)
+ or $log->push, return undef;
+ } else {
+ $server = NDB::Net::ServerINET->new(id => 0, host => $node->getserver->getcanon, port => $node->getdb->getnodeport + $node->getid)
+ or $log->push, return undef;
+ }
+ }
+ my $argv = [
+ $cmdname,
+ $opts, $node->getdb->getname, $node->getid, @args ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $server->request($cmd)
+ or $log->push, return undef;
+ $log->put("$cmdname done")->push($node)->info;
+ return $ret;
+}
+
+sub start {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("start")->push($node)->info;
+ my $do = $opts->{local} ? "dolocal" : "doremote";
+ return $node->$do("startnode", $opts);
+}
+
+sub stop {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("stop")->push($node)->info;
+ my $do = $opts->{local} ? "dolocal" : "doremote";
+ return $node->$do("stopnode", $opts);
+}
+
+sub kill {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("kill")->push($node)->info;
+ my $do = $opts->{local} ? "dolocal" : "doremote";
+ return $node->$do("killnode", $opts);
+}
+
+sub stat {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ $log->put("stat")->push($node)->info;
+ my $do = $opts->{local} ? "dolocal" : "doremote";
+ return $node->$do("statnode", $opts);
+}
+
+sub write {
+ my $node = shift;
+ @_ == 2 or confess 0+@_;
+ my($opts, $text) = @_;
+ $log->put("write: $text")->push($node)->info;
+ my $do = $opts->{local} ? "dolocal" : "doremote";
+ return $node->$do("writenode", $opts, $text);
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeApi.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeApi.pm
new file mode 100644
index 00000000000..08f5f85577d
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeApi.pm
@@ -0,0 +1,84 @@
+package NDB::Net::NodeApi;
+
+use strict;
+use Carp;
+use Symbol;
+
+require NDB::Net::Node;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Node);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::NodeApi->attributes();
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $node = $class->SUPER::new(%attr, type => 'api')
+ or $log->push, return undef;
+ return 1;
+}
+
+# run methods
+
+sub handleprepare {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ my $netenv = NDB::Net::Env->instance;
+ my $envdefs = $node->getenvdefs($opts);
+ defined($envdefs) or return undef;
+ my $nodedir = $node->getnodedir;
+ my $shellfile = $node->getshellfile;
+ my $run;
+ if ($node->hasrun) {
+ $run = $node->getrun;
+ }
+ if (defined($opts->{run})) {
+ $run = $opts->{run};
+ }
+ if (defined($run)) {
+ $log->put("run: $run")->push($node)->user;
+ }
+ if ($^O ne 'MSWin32') {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+cd @{[ $nodedir->getpath ]} || exit 1
+set -x
+exec \$DEBUGGER $run
+END
+ } else {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+cd @{[ $nodedir->getpath ]}
+call $run
+END
+ }
+ return 1;
+}
+
+sub cmd_stopnode_fg {
+ my($node, $cmd) = @_;
+ my $pid = $node->getpid;
+ unless ($pid > 1) {
+ $log->put("bad pid=$pid")->push($node);
+ return undef;
+ }
+ $log->put("kill -15 $pid")->push($node)->user;
+ kill(15, $pid);
+ $node->setstate('stop')
+ or log->push($node), return undef;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeDb.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeDb.pm
new file mode 100644
index 00000000000..88a35ba4f8d
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeDb.pm
@@ -0,0 +1,116 @@
+package NDB::Net::NodeDb;
+
+use strict;
+use Carp;
+use Symbol;
+
+require NDB::Net::Node;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Node);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::NodeDb->attributes(
+ fsdir => sub { s/^\s+|\s+$//g; /^\S+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $node = $class->SUPER::new(%attr, type => 'db')
+ or $log->push, return undef;
+ $node->setfsdir($attr{fsdir})
+ or $log->push, return undef;
+ return 1;
+}
+
+# run methods
+
+sub handleprepare {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ my $netenv = NDB::Net::Env->instance;
+ my $envdefs = $node->getenvdefs($opts);
+ defined($envdefs) or return undef;
+ my $nodedir = $node->getnodedir;
+ my $shellfile = $node->getshellfile;
+ my $fsdir = NDB::Util::Dir->new(
+ path => sprintf("%s/%s/%s-%s.fs",
+ $node->getfsdir, $node->getdb->getname, $node->getid, $node->gettype));
+ $fsdir->mkdir or $log->push, return undef;
+ my $init_rm;
+ my $run;
+ if ($^O ne 'MSWin32') {
+ $init_rm = "# no -i";
+ if ($opts->{init_rm}) {
+ $init_rm = 'rm -f $NDB_FILESYSTEM/*/DBDIH/P0.sysfile';
+ }
+ $run = "\$NDB_TOP/bin/ndb";
+ } else {
+ $init_rm = "rem no -i";
+ if ($opts->{init_rm}) {
+ $init_rm =
+ 'del/f %NDB_FILESYSTEM%\D1\DBDIH\P0.sysfile' . "\n" .
+ 'del/f %NDB_FILESYSTEM%\D2\DBDIH\P0.sysfile';
+ }
+ $run = "ndb";
+ }
+ if ($node->getdb->cmpversion("1.0") <= 0) {
+ $run .= " -s";
+ }
+ if ($opts->{nostart}) {
+ $run .= " -n";
+ }
+ if ($node->hasrun) {
+ $run = $node->getrun;
+ }
+ if (defined($opts->{run})) {
+ $run = $opts->{run};
+ }
+ $log->put("run: $run")->push($node)->user;
+ if ($^O ne 'MSWin32') {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+NDB_FILESYSTEM=@{[ $fsdir->getpath ]}
+export NDB_FILESYSTEM
+# v1.0 compat
+UAS_FILESYSTEM=\$NDB_FILESYSTEM
+export UAS_FILESYSTEM
+mkdir -p \$NDB_FILESYSTEM
+$init_rm
+cd @{[ $nodedir->getpath ]} || exit 1
+exec \$debugger $run
+END
+ } else {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+set NDB_FILESYSTEM=@{[ $fsdir->getpath ]}
+rem v1.0 compat
+set UAS_FILESYSTEM=%NDB_FILESYSTEM%
+mkdir %NDB_FILESYSTEM%
+$init_rm
+cd @{[ $nodedir->getpath ]}
+call $run
+END
+ }
+ return 1;
+}
+
+sub cmd_stopnode_fg {
+ my($node, $cmd) = @_;
+ $node->setstate('stop')
+ or log->push($node), return undef;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeMgmt.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeMgmt.pm
new file mode 100644
index 00000000000..1056e3df623
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeMgmt.pm
@@ -0,0 +1,318 @@
+package NDB::Net::NodeMgmt;
+
+use strict;
+use Carp;
+use Symbol;
+
+require NDB::Net::Node;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Node);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::NodeMgmt->attributes(
+ port => sub { s/^\s+|\s+$//g; /^\d+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $node = $class->SUPER::new(%attr, type => 'mgmt')
+ or $log->push, return undef;
+ $node->setport($attr{port})
+ or $log->push, return undef;
+ return 1;
+}
+
+# socket parser methods
+
+sub socketcommand {
+ my $node = shift;
+ my $socket;
+ $socket = NDB::Util::SocketINET->new or
+ $log->push($node), return undef;
+ $socket->settimeout(10);
+ $socket->connect($node->getserver->getcanon, $node->getport) or
+ $log->push($node), return undef;
+ $socket->write("GET STATUS\r\nBYE\r\n") or
+ $log->push($node), return undef;
+ my $out = "";
+ my $data;
+ while ($data = $socket->read) {
+ $out .= $data;
+ }
+ $socket->close;
+ $out =~ s/\015//g;
+ return $out;
+}
+
+sub get_status {
+ my $node = shift;
+ my $out = $node->socketcommand or
+ $log->push, return undef;
+ my @out = split(/\n/, $out);
+ $out[0] =~ /^get\s+status\s+(\d+)/i or
+ $log->put("bad line 0: $out[0]"), return undef;
+ my $cnt = $1;
+ my $ret = {};
+ for (my $i = 1; $i <= $cnt; $i++) {
+ $out[$i] =~ /^$i\s+(.*)/ or
+ $log->put("bad line $i: $out[$i]"), return undef;
+ my $text = $1;
+ $text =~ s/^\s+|\s+$//g;
+ if ($text =~ /^ndb\s+(no_contact)\s+(\d+)$/i) {
+ $text = lc "$1";
+ } elsif ($text =~ /^ndb\s+(starting)\s+(\d+)$/i) {
+ $text = lc "$1/$2";
+ } elsif ($text =~ /^ndb\s+(started)\s+(\d+)$/i) {
+ $text = lc "$1";
+ } elsif ($text =~ /^ndb\s+(shutting_down)\s+(\d+)$/i) {
+ $text = lc "$1";
+ } elsif ($text =~ /^ndb\s+(restarting)\s+(\d+)$/i) {
+ $text = lc "$1";
+ } elsif ($text =~ /^ndb\s+(unknown)\s+(\d+)$/i) {
+ $text = lc "$1";
+ }
+ $ret->{node}{$i} = $text;
+ }
+ return $ret;
+}
+
+# run methods
+
+sub getautoinifile {
+ my $node = shift;
+ @_ == 0 or confess 0+@_;
+ my $name = "config.txt";
+ my $file = $node->getnodedir->getfile($name);
+ return $file;
+}
+
+sub writeautoinifile {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ my $db = $node->getdb;
+ my $nodelist = $db->getnodelist('all');
+ my $computers = {};
+ for my $n (@$nodelist) {
+ $computers->{$n->getserver->getid} ||= {
+ id => $n->getserver->getid,
+ hostname => $n->getserver->getcanon,
+ };
+ }
+ my $section = ""; # e.g. PROCESSES
+ my $auto;
+ my $edit = sub {
+ chomp;
+ s/^\s+|\s+$//g;
+ if (/^(\w+)$/) {
+ $section = uc($1);
+ }
+ elsif (/^\@loop$/i) {
+ $_ = "#$_";
+ if ($auto) {
+ $log->put("nested \@loop");
+ return undef;
+ }
+ $auto = {};
+ }
+ elsif (/^\@base\s+(\S+)\s*$/) {
+ my $arg = $1;
+ $_ = "#$_";
+ if (! $auto) {
+ $log->put("unexpected \@base");
+ return undef;
+ }
+ if ($arg !~ /^\d+$/) {
+ $log->put("non-numerical \@base");
+ return undef;
+ }
+ $auto->{base} = $arg;
+ }
+ elsif (/^\@end$/i) {
+ $_ = "#$_";
+ if (! $auto) {
+ $log->put("unmatched \@end");
+ return undef;
+ }
+ if ($section eq 'COMPUTERS') {
+ for my $id (sort { $a <=> $b } keys %$computers) {
+ my $computer = $computers->{$id};
+ $_ .= "\n";
+ $_ .= "\nId: " . $computer->{id};
+ $_ .= "\nHostName: " . $computer->{hostname};
+ if ($auto->{list}) {
+ $_ .= "\n#defaults";
+ for my $s (@{$auto->{list}}) {
+ $_ .= "\n$s";
+ }
+ }
+ }
+ }
+ elsif ($section eq 'PROCESSES') {
+ for my $n (@$nodelist) {
+ if ($auto->{type} && $n->gettype ne lc($auto->{type})) {
+ next;
+ }
+ $_ .= "\n";
+ $_ .= "\nType: " . uc($n->gettype);
+ $_ .= "\nId: " . $n->getid;
+ $_ .= "\nExecuteOnComputer: " . $n->getserver->getid;
+ if ($auto->{list}) {
+ $_ .= "\n#defaults";
+ for my $s (@{$auto->{list}}) {
+ $_ .= "\n$s";
+ }
+ }
+ }
+ }
+ elsif ($section eq 'CONNECTIONS') {
+ if (! $auto->{type}) {
+ $log->put("cannot generate CONNECTIONS without type");
+ return undef;
+ }
+ if (! defined($auto->{base})) {
+ $log->put("need \@base for CONNECTIONS");
+ return undef;
+ }
+ my $key = $auto->{base};
+ for (my $i1 = 0; $i1 <= $#$nodelist; $i1++) {
+ for (my $i2 = $i1+1; $i2 <= $#$nodelist; $i2++) {
+ my $n1 = $nodelist->[$i1];
+ my $n2 = $nodelist->[$i2];
+ if ($n1->gettype ne 'db' && $n2->gettype ne 'db') {
+ next;
+ }
+ $_ .= "\n";
+ $_ .= "\nType: $auto->{type}";
+ $_ .= "\nProcessId1: " . $n1->getid;
+ $_ .= "\nProcessId2: " . $n2->getid;
+ $key++;
+ if ($auto->{type} eq 'TCP') {
+ $_ .= "\nPortNumber: $key";
+ if (my $list = $opts->{proxy}) {
+ my $id1 = $n1->getid;
+ my $id2 = $n2->getid;
+ if ($list =~ /\b$id1\b.*-.*\b$id2\b/) {
+ $key++;
+ $_ .= "\nProxy: $key";
+ } elsif ($list =~ /\b$id2\b.*-.*\b$id1\b/) {
+ $key++;
+ $_ .= "\nProxy: $key";
+ }
+ }
+ }
+ elsif ($auto->{type} eq 'SHM') {
+ $_ .= "\nShmKey: $key";
+ }
+ else {
+ $log->put("cannot handle CONNECTIONS type $auto->{type}");
+ return undef;
+ }
+ if ($auto->{list}) {
+ $_ .= "\n#defaults";
+ for my $s (@{$auto->{list}}) {
+ $_ .= "\n$s";
+ }
+ }
+ }
+ }
+ }
+ else {
+ $log->put("found \@end in unknown section '$section'");
+ return undef;
+ }
+ undef $auto;
+ }
+ elsif (/^$/) {
+ }
+ elsif ($auto) {
+ if (/^Type:\s*(\w+)$/i) {
+ $auto->{type} = uc($1);
+ }
+ else {
+ $auto->{list} ||= [];
+ push(@{$auto->{list}}, $_);
+ }
+ $_ = "";
+ return 1; # no output
+ }
+ $_ .= "\n";
+ return 1;
+ };
+ $node->getautoinifile->mkdir
+ or $log->push, return undef;
+ $node->getinifile->copyedit($node->getautoinifile, $edit)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub handleprepare {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($opts) = @_;
+ my $envdefs = $node->getenvdefs($opts);
+ defined($envdefs) or return undef;
+ my $nodedir = $node->getnodedir;
+ my $shellfile = $node->getshellfile;
+ my $port = $node->getport;
+ my $lpath = $node->getlocalcfg->getbasename;
+ $node->writeautoinifile($opts)
+ or $log->push, return undef;
+ my $ipath = $node->getautoinifile->getbasename;
+ $node->getbincfg->mkdir or $log->push, return undef;
+ my $cpath = $node->getbincfg->getbasename;
+ my $run;
+ if ($^O ne 'MSWin32') {
+ $run = "\$NDB_TOP/bin/mgmtsrvr";
+ } else {
+ $run = "mgmtsrvr";
+ }
+ my $statport = $port + 1;
+ $run .= " -l $lpath -c $ipath";
+ if ($node->hasrun) {
+ $run = $node->getrun;
+ }
+ if (defined($opts->{run})) {
+ $run = $opts->{run};
+ }
+ $log->put("run: $run")->push($node)->user;
+ if ($^O ne 'MSWin32') {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+cd @{[ $nodedir->getpath ]} || exit 1
+set -x
+exec \$DEBUGGER $run
+END
+ } else {
+ $shellfile->puttext(<<END) or $log->push, return undef;
+$envdefs
+cd @{[ $nodedir->getpath ]}
+call $run
+END
+ }
+ return 1;
+}
+
+sub cmd_stopnode_fg {
+ my $node = shift;
+ @_ == 1 or confess 0+@_;
+ my($cmd) = @_;
+ $log->put("write: quit")->push($node)->user;
+ $node->getiow->write("quit\n");
+ $node->setstate('stop')
+ or log->push($node), return undef;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Server.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Server.pm
new file mode 100644
index 00000000000..5d2118f0ffe
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Server.pm
@@ -0,0 +1,149 @@
+package NDB::Net::Server;
+
+use strict;
+use Carp;
+use Socket;
+
+require NDB::Net::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+my %servercache = ();
+
+NDB::Net::Server->attributes(
+ id => sub { s/^\s+|\s+$//g; m/^\S+$/ && ! m!/! },
+ domain => sub { $_ == PF_UNIX || $_ == PF_INET },
+);
+
+sub desc {
+ my $server = shift;
+ my $id = $server->getid;
+ return "server $id";
+}
+
+sub add {
+ my $server = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ if ($servercache{$server->getid}) {
+ $log->put("duplicate server")->push($server);
+ return undef;
+ }
+ $servercache{$server->getid} = $server;
+ return 1;
+}
+
+sub get {
+ my $class = shift;
+ @_ == 1 or confess 0+@_;
+ my($id) = @_;
+ $id =~ s/^\s+|\s+$//g;
+ my $server = $servercache{$id};
+ if (! $server) {
+ $log->put("$id: undefined server");
+ return undef;
+ }
+ $log->put("found")->push($server)->debug;
+ return $server;
+}
+
+sub delete {
+ my $server = shift;
+ delete $servercache{$server->getid};
+}
+
+sub deleteall {
+ my $class = shift;
+ for my $id (sort keys %servercache) {
+ my $server = $servercache{$id};
+ $server->delete;
+ }
+}
+
+# local server is this server process
+
+my $localserver;
+
+sub setlocal {
+ my $server = shift;
+ @_ == 0 or confess 0+@_;
+ $localserver = $server;
+}
+
+sub islocal {
+ my $server = shift;
+ @_ == 0 or confess 0+@_;
+ return $localserver eq $server;
+}
+
+# client side
+
+sub testconnect {
+ my $server = shift;
+ @_ == 0 or confess 0+@_;
+ my $socket = $server->connect or
+ $log->push($server), return undef;
+ $socket->close;
+ return 1;
+}
+
+sub request {
+ my $server = shift;
+ @_ == 1 or confess 0+@_;
+ my($cmd) = @_;
+ unless (ref($cmd) && $cmd->isa('NDB::Net::Command')) {
+ confess 'oops';
+ }
+ my $socket = $server->connect
+ or $log->push($server), return undef;
+ anon: {
+ my $line = $cmd->getline;
+ my $n = $socket->write("$line\n");
+ defined($n) && $n == length("$line\n")
+ or $log->push($server), return undef;
+ shutdown($socket->{fh}, 1);
+ }
+ my $value;
+ try: {
+ my $last;
+ loop: {
+ my $line = $socket->readline;
+ defined($line)
+ or $log->push($server), last try;
+ if ($socket->getreadend) {
+ last loop;
+ }
+ while (chomp($line)) {}
+ $log->put($line)->user
+ unless $log->hasvalue($line);
+ $last = $line;
+ redo loop;
+ }
+ if (! $log->hasvalue($last)) {
+ $log->put("missing return value in \"$last\"")->push($server);
+ last try;
+ }
+ $value = $log->getvalue($last);
+ defined($value)
+ or $log->push, last try;
+ $value = $value->[0];
+ if (! defined($value)) {
+ $log->put("failed")->push($cmd);
+ last try;
+ }
+ }
+ $socket->close;
+ return $value;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerINET.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerINET.pm
new file mode 100644
index 00000000000..a065c186855
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerINET.pm
@@ -0,0 +1,116 @@
+package NDB::Net::ServerINET;
+
+use strict;
+use Carp;
+use Socket;
+
+require NDB::Net::Server;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Server);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::ServerINET->attributes(
+ host => sub { s/^\s+|\s+$//g; /^\S+$/ },
+ port => sub { s/^\s+|\s+$//g; /^\d+$/ },
+ canon => sub { s/^\s+|\s+$//g; /^\S+$/ },
+ aliases => sub { ref($_) eq 'ARRAY' },
+);
+
+
+sub desc {
+ my $server = shift;
+ my $id = $server->getid;
+ my $host = $server->gethost;
+ return "server $id at $host";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $server = $class->SUPER::new(%attr);
+ $server->setid($attr{id})
+ or $log->push, return undef;
+ $server->setdomain(PF_INET)
+ or $log->push, return undef;
+ $server->sethost($attr{host})
+ or $log->push, return undef;
+ $server->setport($attr{port})
+ or $log->push, return undef;
+ my($canon, $aliases) = gethostbyname($server->gethost);
+ if (! defined($canon)) {
+ $log->put("%s: unknown host", $server->gethost);
+ return undef;
+ }
+ $server->setcanon($canon)
+ or $log->push, return undef;
+ $server->setaliases([ split(' ', $aliases) ])
+ or $log->push, return undef;
+ $server->add or
+ $log->push, return undef;
+ $log->put("added")->push($server)->debug;
+ return $server;
+}
+
+# find matching servers
+
+sub match {
+ my $class = shift;
+ @_ == 3 or confess 0+@_;
+ my($host, $port, $servers) = @_;
+ if (! defined($port) && $host =~ /:/) {
+ ($host, $port) = split(/:/, $host, 2);
+ }
+ $host =~ s/^\s+|\s+$//g;
+ my($canon) = gethostbyname($host);
+ unless (defined($canon)) {
+ $log->put("$host: unknown host");
+ return undef;
+ }
+ my $hostport = $host;
+ if (defined($port)) {
+ $port =~ s/^\s+|\s+$//g;
+ $port =~ /\d+$/
+ or $log->put("$port: non-numeric port"), return undef;
+ $hostport .= ":$port";
+ }
+ my @server = ();
+ for my $s (@$servers) {
+ ($s->getdomain == PF_INET) || next;
+ ($s->getcanon eq $canon) || next;
+ ($port && $s->getport != $port) && next;
+ push(@server, $s);
+ }
+ if (! @server) {
+ $log->put("$hostport: no server found");
+ }
+ if (@server > 1) {
+ $log->put("$hostport: multiple servers at ports ",
+ join(' ', map($_->getport, @server)));
+ }
+ return \@server;
+}
+
+# client side
+
+sub connect {
+ my $server = shift;
+ @_ == 0 or confess 0+@_;
+ my $socket;
+ $socket = NDB::Util::SocketINET->new or
+ $log->push, return undef;
+ $socket->connect($server->gethost, $server->getport) or
+ $log->push, return undef;
+ return $socket;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerUNIX.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerUNIX.pm
new file mode 100644
index 00000000000..b3fa245d5ee
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerUNIX.pm
@@ -0,0 +1,54 @@
+package NDB::Net::ServerUNIX;
+
+use strict;
+use Carp;
+use Socket;
+
+require NDB::Net::Server;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Net::Server);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Net::ServerUNIX->attributes(
+ path => sub { s/^\s+|\s+$//g; /^\S+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $server = $class->SUPER::new(%attr);
+ $server->setid($attr{id})
+ or $log->push, return undef;
+ $server->setdomain(PF_UNIX)
+ or $log->push, return undef;
+ $server->setpath($attr{path})
+ or $log->push, return undef;
+ $server->add or
+ $log->push, return undef;
+ return $server;
+}
+
+# client side
+
+sub connect {
+ my $server = shift;
+ @_ == 0 or confess 0+@_;
+ my $socket;
+ $socket = NDB::Util::SocketUNIX->new or
+ $log->push, return undef;
+ $socket->connect($server->getpath) or
+ $log->push, return undef;
+ return $socket;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run.pm
new file mode 100644
index 00000000000..a8cabde544c
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run.pm
@@ -0,0 +1,40 @@
+package NDB::Run;
+
+use strict;
+use Carp;
+require Exporter;
+
+use NDB::Net;
+
+use vars qw(@ISA @EXPORT @EXPORT_OK);
+@ISA = qw(Exporter);
+
+use vars qw(@modules);
+@modules = qw(
+ NDB::Run::Base
+ NDB::Run::Database
+ NDB::Run::Env
+ NDB::Run::Node
+);
+
+return 1 if $main::onlymodules;
+
+for my $module (@modules) {
+ eval "require $module";
+ $@ and confess "$module $@";
+}
+
+for my $module (@modules) {
+ eval "$module->initmodule";
+ $@ and confess "$module $@";
+}
+
+# methods
+
+sub getenv {
+ my $class = shift;
+ return NDB::Run::Env->new(@_);
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Base.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Base.pm
new file mode 100644
index 00000000000..4769f2c4441
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Base.pm
@@ -0,0 +1,12 @@
+package NDB::Run::Base;
+
+use strict;
+use Carp;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Database.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Database.pm
new file mode 100644
index 00000000000..9a12ddb20b3
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Database.pm
@@ -0,0 +1,89 @@
+package NDB::Run::Database;
+
+use strict;
+use Carp;
+
+require NDB::Run::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Run::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Run::Database->attributes(
+ name => sub { s/^\s+|\s+$//g; /^\S+$/ && ! m!/! },
+ env => sub { ref && $_->isa('NDB::Run::Env') },
+);
+
+sub desc {
+ my $db = shift;
+ return $db->getname;
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $db = $class->SUPER::new(%attr);
+ $db->setname($attr{name})
+ or $log->push, return undef;
+ $db->setenv($attr{env})
+ or $log->push, return undef;
+ return $db;
+}
+
+sub getnode {
+ my $db = shift;
+ @_ == 1 or croak q(usage: $node = $db->getnode($id));
+ my($id) = @_;
+ my $node = NDB::Run::Node->new(db => $db, id => $id)
+ or $log->push, return undef;
+ return $node;
+}
+
+# commands
+
+sub start {
+ my $db = shift;
+ my $opts = shift;
+ my $argv = [ 'start', $db->getname, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $db->getenv->docmd($cmd);
+ defined($ret)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub stop {
+ my $db = shift;
+ my $opts = shift;
+ my $argv = [ 'stop', $db->getname, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $db->getenv->docmd($cmd);
+ defined($ret)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub kill {
+ my $db = shift;
+ my $opts = shift;
+ my $argv = [ 'kill', $db->getname, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $db->getenv->docmd($cmd);
+ defined($ret)
+ or $log->push, return undef;
+ return $ret;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Env.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Env.pm
new file mode 100644
index 00000000000..e851a82636b
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Env.pm
@@ -0,0 +1,84 @@
+package NDB::Run::Env;
+
+use strict;
+use Carp;
+
+require NDB::Run::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Run::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Run::Env->attributes(
+ server => sub { ref && $_->isa('NDB::Net::Server') },
+);
+
+sub desc {
+ "env";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $env = $class->SUPER::new(%attr);
+ return $env;
+}
+
+sub getdb {
+ my $env = shift;
+ @_ == 1 or croak q(usage: $db = $env->getdb($name));
+ my($name) = @_;
+ my $db = NDB::Run::Database->new(env => $env, name => $name)
+ or $log->push, return undef;
+ return $db;
+}
+
+# commands
+
+sub init {
+ my $env = shift;
+ my $netenv = NDB::Net::Env->instance;
+ my $netcfg = NDB::Net::Config->new(file => $netenv->getnetcfg)
+ or $log->push, return undef;
+ $netcfg->load
+ or $log->push, return undef;
+ my $servers = $netcfg->getservers
+ or $log->push, return undef;
+ my $server;
+ for my $s (@$servers) {
+ if (! $s->testconnect) {
+ $log->push->warn;
+ next;
+ }
+ $server = $s;
+ last;
+ }
+ if (! $server) {
+ $log->put("no available server")->push($netcfg);
+ return undef;
+ }
+ $env->setserver($server)
+ or $log->push, return undef;
+ $log->put("selected")->push($server)->info;
+ return 1;
+}
+
+sub docmd {
+ my $env = shift;
+ my $cmd = shift;
+ my $ret = $env->getserver->request($cmd);
+ defined($ret)
+ or $log->push, return undef;
+ return $ret;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Node.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Node.pm
new file mode 100644
index 00000000000..e657021b229
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Node.pm
@@ -0,0 +1,114 @@
+package NDB::Run::Node;
+
+use strict;
+use Carp;
+
+require NDB::Run::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Run::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Run::Node->attributes(
+ env => sub { ref && $_->isa('NDB::Run::Env') },
+ db => sub { ref && $_->isa('NDB::Run::Database') },
+ dbname => sub { s/^\s+|\s+$//g; /^\S+$/ && ! m!/! },
+ id => sub { s/^\s+|\s+$//g; s/^0+(\d+)$/$1/; /^\d+$/ && $_ > 0 },
+ type => sub { s/^\s+|\s+$//g; /^(mgmt|db|api)$/ },
+);
+
+sub desc {
+ my $node = shift;
+ my $dbname = $node->getdb->getname;
+ my $id = $node->getid;
+ my $type = "?"; # $node->gettype;
+ return "$dbname.$id-$type";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $node = $class->SUPER::new(%attr);
+ $node->setdb($attr{db})
+ or $log->push, return undef;
+ $node->setenv($node->getdb->getenv)
+ or $log->push, return undef;
+ $node->setdbname($node->getdb->getname)
+ or $log->push, return undef;
+ $node->setid($attr{id})
+ or $log->push, return undef;
+# $node->settype($attr{type})
+# or $log->push, return undef;
+ return $node;
+}
+
+# commands
+
+sub start {
+ my $node = shift;
+ my $opts = shift;
+ my $argv = [ 'startnode', $node->getdb->getname, $node->getid, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $node->getenv->docmd($cmd)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub stop {
+ my $node = shift;
+ my $opts = shift;
+ my $argv = [ 'stopnode', $node->getdb->getname, $node->getid, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $node->getenv->docmd($cmd)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub kill {
+ my $node = shift;
+ my $opts = shift;
+ my $argv = [ 'killnode', $node->getdb->getname, $node->getid, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $node->getenv->docmd($cmd)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub stat {
+ my $node = shift;
+ my $opts = shift;
+ my $argv = [ 'statnode', $node->getdb->getname, $node->getid, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $node->getenv->docmd($cmd)
+ or $log->push, return undef;
+ return $ret;
+}
+
+sub write {
+ my $node = shift;
+ my $text = shift;
+ my $opts = shift;
+ my $argv = [ 'writenode', $node->getdb->getname, $node->getid, $text, $opts ];
+ my $cmd = NDB::Net::Command->new(argv => $argv)
+ or $log->push, return undef;
+ my $ret = $node->getenv->docmd($cmd)
+ or $log->push, return undef;
+ ref($ret) eq 'HASH' && defined($ret->{output})
+ or confess 'oops';
+ return $ret;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util.pm
new file mode 100644
index 00000000000..d5db35cbf13
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util.pm
@@ -0,0 +1,37 @@
+package NDB::Util;
+
+use strict;
+use Carp;
+require Exporter;
+
+use vars qw(@ISA @EXPORT @EXPORT_OK);
+@ISA = qw(Exporter);
+
+use vars qw(@modules);
+@modules = qw(
+ NDB::Util::Base
+ NDB::Util::Dir
+ NDB::Util::Event
+ NDB::Util::File
+ NDB::Util::IO
+ NDB::Util::Lock
+ NDB::Util::Log
+ NDB::Util::Socket
+ NDB::Util::SocketINET
+ NDB::Util::SocketUNIX
+);
+
+return 1 if $main::onlymodules;
+
+for my $module (@modules) {
+ eval "require $module";
+ $@ and confess "$module $@";
+}
+
+for my $module (@modules) {
+ eval "$module->initmodule";
+ $@ and confess "$module $@";
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Base.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Base.pm
new file mode 100644
index 00000000000..20df78a3b9b
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Base.pm
@@ -0,0 +1,113 @@
+package NDB::Util::Base;
+
+use strict;
+use Carp;
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+sub new {
+ my $class = shift;
+ my $this = bless {}, $class;
+ return $this;
+}
+
+sub getlog {
+ my $this = shift;
+ return NDB::Util::Log->instance;
+}
+
+# clone an object
+# extra attributes override or delete (if value is undef)
+sub clone {
+ my $this = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $that = bless {}, ref($this);
+ for my $attr (sort keys %$this) {
+ if (! exists($attr{$attr})) {
+ my $get = "get$attr";
+ $attr{$attr} = $this->$get();
+ }
+ }
+ for my $attr (sort keys %attr) {
+ if (defined($attr{$attr})) {
+ my $set = "set$attr";
+ $that->$set($attr{$attr});
+ }
+ }
+ return $that;
+}
+
+# methods for member variables:
+# - set returns 1 on success and undef on undefined or invalid value
+# - get aborts unless value exists or a default (maybe undef) is given
+# - has tests existence of value
+# - del deletes the value and returns it (maybe undef)
+
+sub attributes {
+ @_ % 2 == 1 or confess 0+@_;
+ my $class = shift;
+ my @attr = @_;
+ while (@attr) {
+ my $attr = shift @attr;
+ my $filter = shift @attr;
+ $attr =~ /^\w+$/ or confess $attr;
+ ref($filter) eq 'CODE' or confess $attr;
+ my $set = sub {
+ @_ == 2 or confess "set$attr: arg count: @_";
+ my $this = shift;
+ my $value = shift;
+ if (! defined($value)) {
+ $log->put("set$attr: undefined value")->push($this);
+ return undef;
+ }
+ local $_ = $value;
+ if (! &$filter($this)) {
+ $log->put("set$attr: invalid value: $value")->push($this);
+ return undef;
+ }
+ $value = $_;
+ if (! defined($value)) {
+ confess "set$attr: changed to undef";
+ }
+ $this->{$attr} = $value;
+ return 1;
+ };
+ my $get = sub {
+ @_ == 1 || @_ == 2 or confess "get$attr: arg count: @_";
+ my $this = shift;
+ my $value = $this->{$attr};
+ if (! defined($value)) {
+ @_ == 0 and confess "get$attr: no value";
+ $value = shift;
+ }
+ return $value;
+ };
+ my $has = sub {
+ @_ == 1 or confess "has$attr: arg count: @_";
+ my $this = shift;
+ my $value = $this->{$attr};
+ return defined($value);
+ };
+ my $del = sub {
+ @_ == 1 or confess "del$attr: arg count: @_";
+ my $this = shift;
+ my $value = delete $this->{$attr};
+ return $value;
+ };
+ no strict 'refs';
+ *{"${class}::set$attr"} = $set;
+ *{"${class}::get$attr"} = $get;
+ *{"${class}::has$attr"} = $has;
+ *{"${class}::del$attr"} = $del;
+ }
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Dir.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Dir.pm
new file mode 100644
index 00000000000..90609b971c7
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Dir.pm
@@ -0,0 +1,170 @@
+package NDB::Util::Dir;
+
+use strict;
+use Carp;
+use Symbol;
+use Errno;
+use File::Basename;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::Dir->attributes(
+ path => sub { length > 0 },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $dir = $class->SUPER::new(%attr);
+ $dir->setpath($attr{path})
+ or $log->push, return undef;
+ return $dir;
+}
+
+sub desc {
+ my $dir = shift;
+ return $dir->getpath;
+}
+
+sub getparent {
+ my $dir = shift;
+ @_ == 0 or confess 0+@_;
+ my $ppath = dirname($dir->getpath);
+ my $pdir = NDB::Util::Dir->new(path => $ppath);
+ return $pdir;
+}
+
+sub getdir {
+ my $dir = shift;
+ @_ == 1 or confess 0+@_;
+ my($name) = @_;
+ my $dirpath = $dir->getpath;
+ my $path = $dirpath eq '.' ? $name : File::Spec->catfile($dirpath, $name);
+ my $entry = NDB::Util::Dir->new(path => $path);
+ return $entry;
+}
+
+sub getfile {
+ my $dir = shift;
+ @_ == 1 or confess 0+@_;
+ my($name) = @_;
+ my $dirpath = $dir->getpath;
+ my $path = $dirpath eq '.' ? $name : File::Spec->catfile($dirpath, $name);
+ my $entry = NDB::Util::File->new(path => $path);
+ return $entry;
+}
+
+# list
+
+sub listdirs {
+ my $dir = shift;
+ @_ == 0 or confess 0+@_;
+ my @list = ();
+ my $dirpath = $dir->getpath;
+ my $dh = gensym();
+ if (! opendir($dh, $dirpath)) {
+ $log->put("opendir failed: $!")->push($dir);
+ return undef;
+ }
+ while (defined(my $name = readdir($dh))) {
+ if ($name eq '.' || $name eq '..') {
+ next;
+ }
+ my $path = $dirpath eq '.' ? $name : "$dirpath/$name";
+ if (! -l $path && -d $path) {
+ my $dir2 = NDB::Util::Dir->new(path => $path)
+ or $log->push, return undef;
+ push(@list, $dir2);
+ }
+ }
+ close($dh);
+ return \@list;
+}
+
+sub listfiles {
+ my $dir = shift;
+ @_ == 0 or confess 0+@_;
+ my @list = ();
+ my $dirpath = $dir->getpath;
+ my $dh = gensym();
+ if (! opendir($dh, $dirpath)) {
+ $log->put("opendir failed: $!")->push($dir);
+ return undef;
+ }
+ while (defined(my $name = readdir($dh))) {
+ if ($name eq '.' || $name eq '..') {
+ next;
+ }
+ my $path = $dirpath eq '.' ? $name : "$dirpath/$name";
+ if (! -d $path && -e $path) {
+ my $file2 = NDB::Util::File->new(path => $path)
+ or $log->push, return undef;
+ push(@list, $file2);
+ }
+ }
+ close($dh);
+ return \@list;
+}
+
+# create / remove
+
+sub mkdir {
+ my $dir = shift;
+ @_ == 0 or confess 0+@_;
+ if (! -d $dir->getpath) {
+ my $pdir = $dir->getparent;
+ if (length($pdir->getpath) >= length($dir->getpath)) {
+ $log->put("mkdir looping")->push($dir);
+ return undef;
+ }
+ $pdir->mkdir or return undef;
+ if (! mkdir($dir->getpath, 0777)) {
+ my $errstr = "$!";
+ if (-d $dir->getpath) {
+ return 1;
+ }
+ $log->put("mkdir failed: $errstr")->push($dir);
+ return undef;
+ }
+ }
+ return 1;
+}
+
+sub rmdir {
+ my $dir = shift;
+ my $keep = shift; # keep top level
+ $log->put("remove")->push($dir)->info;
+ my $list;
+ $list = $dir->listdirs or $log->push, return undef;
+ for my $d (@$list) {
+ $d->rmdir or $log->push, return undef;
+ }
+ $list = $dir->listfiles or $log->push, return undef;
+ for my $f (@$list) {
+ $f->unlink or $log->push, return undef;
+ }
+ if (! $keep && ! rmdir($dir->getpath)) {
+ my $errstr = "$!";
+ if (! -e $dir->getpath) {
+ return 1;
+ }
+ $log->put("rmdir failed: $errstr")->push($dir);
+ return undef;
+ }
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Event.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Event.pm
new file mode 100644
index 00000000000..a3ad32cd7fb
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Event.pm
@@ -0,0 +1,103 @@
+package NDB::Util::Event;
+
+use strict;
+use Carp;
+use Errno;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::Event->attributes();
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $event = $class->SUPER::new(%attr);
+ return $event;
+}
+
+# set and test bits
+
+sub check {
+ my $event = shift;
+ my($file, $type) = @_;
+ my $fileno;
+ if (ref($file) eq 'GLOB') {
+ $fileno = fileno($file);
+ }
+ elsif (ref($file)) {
+ $file->can("getfh") or confess 'oops';
+ $fileno = fileno($file->getfh);
+ }
+ else {
+ $fileno = $file;
+ }
+ defined($fileno) or confess 'oops';
+ $fileno =~ s/^\s+|\s+$//g;
+ $fileno =~ /^\d+$/ or confess 'oops';
+ $type =~ /^[rwe]$/ or confess 'oops';
+ return ($fileno, $type);
+}
+
+sub set {
+ my $event = shift;
+ @_ == 2 or confess 0+@_;
+ my($fileno, $type) = $event->check(@_);
+ vec($event->{"i_$type"}, $fileno, 1) = 1;
+}
+
+sub clear {
+ my $event = shift;
+ @_ == 2 or confess 0+@_;
+ my($fileno, $type) = $event->check(@_);
+ vec($event->{"i_$type"}, $fileno, 1) = 0;
+}
+
+sub test {
+ my $event = shift;
+ @_ == 2 or confess 0+@_;
+ my($fileno, $type) = $event->check(@_);
+ return vec($event->{"o_$type"}, $fileno, 1);
+}
+
+# poll
+
+sub poll {
+ my $event = shift;
+ @_ <= 1 or confess 'oops';
+ my $timeout = shift;
+ if (defined($timeout)) {
+ $timeout =~ /^\d+$/ or confess 'oops';
+ }
+ $event->{o_r} = $event->{i_r};
+ $event->{o_w} = $event->{i_w};
+ $event->{o_e} = $event->{i_e};
+ my $n;
+ $n = select($event->{o_r}, $event->{o_w}, $event->{o_e}, $timeout);
+ if ($n < 0 || ! defined($n)) {
+ if ($! == Errno::EINTR) {
+ $log->put("select interrupted");
+ return 0;
+ }
+ $log->put("select failed: $!");
+ return undef;
+ }
+ if (! $n) {
+ $log->put("select timed out");
+ }
+ return $n;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/File.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/File.pm
new file mode 100644
index 00000000000..4b3cb38191c
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/File.pm
@@ -0,0 +1,163 @@
+package NDB::Util::File;
+
+use strict;
+use Carp;
+use Symbol;
+use Errno;
+use File::Basename;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::File->attributes(
+ path => sub { length > 0 },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $file = $class->SUPER::new(%attr);
+ $file->setpath($attr{path})
+ or $log->push, return undef;
+ return $file;
+}
+
+sub desc {
+ my $file = shift;
+ return $file->getpath;
+}
+
+sub getdir {
+ my $file = shift;
+ @_ == 0 or confess 0+@_;
+ my $dirpath = dirname($file->getpath);
+ my $dir = NDB::Util::Dir->new(path => $dirpath);
+ return $dir;
+}
+
+sub getlock {
+ my $file = shift;
+ @_ == 0 or confess 0+@_;
+ my $lock = NDB::Util::Lock->new(path => $file->getpath);
+ return $lock;
+}
+
+sub getbasename {
+ my $file = shift;
+ @_ == 0 or confess 0+@_;
+ return basename($file->getpath);
+}
+
+# make dir, unlink
+
+sub mkdir {
+ my $file = shift;
+ @_ == 0 or confess 0+@_;
+ return $file->getdir->mkdir;
+}
+
+sub unlink {
+ my $file = shift;
+ @_ == 0 or confess 0+@_;
+ $log->put("remove")->push($file)->debug;
+ if (-e $file->getpath) {
+ if (! unlink($file->getpath)) {
+ my $errstr = "$!";
+ if (! -e $file->getpath) {
+ return 1;
+ }
+ $log->put("unlink failed: $errstr")->push($file);
+ return undef;
+ }
+ }
+ return 1;
+}
+
+# read /write
+
+sub open {
+ my $file = shift;
+ @_ == 1 or confess 0+@_;
+ my($mode) = @_;
+ my $fh = gensym();
+ if (! open($fh, $mode.$file->getpath)) {
+ $log->put("open$mode failed")->push($file);
+ return undef;
+ }
+ my $io = NDB::Util::IO->new;
+ $io->setfh($fh)
+ or $log->push, return undef;
+ return $io;
+}
+
+sub puttext {
+ my $file = shift;
+ @_ == 1 or confess 0+@_;
+ my($text) = @_;
+ ref($text) and confess 'oops';
+ $file->mkdir
+ or $log->push, return undef;
+ $file->unlink
+ or $log->push, return undef;
+ my $io = $file->open(">")
+ or $log->push, return undef;
+ if (! $io->write($text)) {
+ $log->push($file);
+ $io->close;
+ return undef;
+ }
+ if (! $io->close) {
+ $log->push($file);
+ return undef;
+ }
+ return 1;
+}
+
+sub putlines {
+ my $file = shift;
+ @_ == 1 or confess 0+@_;
+ my($lines) = @_;
+ ref($lines) eq 'ARRAY' or confess 'oops';
+ my $text = join("\n", @$lines) . "\n";
+ $file->puttext($text) or $log->push, return undef;
+ return 1;
+}
+
+sub copyedit {
+ my $file1 = shift;
+ @_ == 2 or confess 0+@_;
+ my($file2, $edit) = @_;
+ my $io1 = $file1->open("<")
+ or $log->push, return undef;
+ my $io2 = $file2->open(">")
+ or $log->push, return undef;
+ local $_;
+ my $fh1 = $io1->getfh;
+ my $fh2 = $io2->getfh;
+ my $line = 0;
+ while (defined($_ = <$fh1>)) {
+ $line++;
+ if (! &$edit()) {
+ $log->push("line $line")->push($file1);
+ return undef;
+ }
+ print $fh2 $_;
+ }
+ $io1->close;
+ $io2->close;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/IO.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/IO.pm
new file mode 100644
index 00000000000..34f4d0a150d
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/IO.pm
@@ -0,0 +1,213 @@
+package NDB::Util::IO;
+
+use strict;
+use Carp;
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::IO->attributes(
+ readbuf => sub { defined },
+ readend => sub { defined },
+ writebuf => sub { defined },
+ writeend => sub { defined },
+ iosize => sub { $_ > 0 },
+ timeout => sub { /^\d+$/ },
+ fh => sub { ref($_) eq 'GLOB' && defined(fileno($_)) },
+);
+
+sub desc {
+ my $io = shift;
+ my $fileno = $io->hasfh ? fileno($io->getfh) : -1;
+ return "fd=$fileno";
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $io = $class->SUPER::new(%attr);
+ $io->setreadbuf("")
+ or $log->push, return undef;
+ $io->setreadend(0)
+ or $log->push, return undef;
+ $io->setwritebuf("")
+ or $log->push, return undef;
+ $io->setwriteend(0)
+ or $log->push, return undef;
+ $io->setiosize(1024)
+ or $log->push, return undef;
+ $io->settimeout(0)
+ or $log->push, return undef;
+ if (defined($attr{fh})) {
+ $io->setfh($attr{fh})
+ or $log->push, return undef;
+ }
+ return $io;
+}
+
+# input / output
+
+sub read {
+ my $io = shift;
+ @_ == 0 or confess 0+@_;
+ if ($io->getreadend) {
+ return "";
+ }
+ my $size = $io->getiosize;
+ my $timeout = $io->hastimeout ? $io->gettimeout : 0;
+ my $fh = $io->getfh;
+ my $n;
+ my $data;
+ eval {
+ if ($^O ne 'MSWin32' && $timeout > 0) {
+ local $SIG{ALRM} = sub { die("timed out\n") };
+ alarm($timeout);
+ $n = sysread($fh, $data, $size);
+ alarm(0);
+ }
+ else {
+ $n = sysread($fh, $data, $size);
+ }
+ };
+ if ($@) {
+ $log->put("read error: $@")->push($io);
+ return undef;
+ }
+ if (! defined($n)) {
+ $log->put("read failed: $!")->push($io);
+ return undef;
+ }
+ if ($n == 0) {
+ $io->setreadend(1)
+ or $log->push, return undef;
+ $log->put("read EOF")->push($io)->debug;
+ return "";
+ }
+ (my $show = $data) =~ s!\n!\\n!g;
+ $log->put("read: $show")->push($io)->debug;
+ return $data;
+}
+
+sub readbuf {
+ my $io = shift;
+ @_ == 0 or confess 0+@_;
+ my $data = $io->read;
+ defined($data) or
+ $log->push, return undef;
+ if (length($data) == 0) {
+ return 0;
+ }
+ $io->setreadbuf($io->getreadbuf . $data)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub readupto {
+ my $io = shift;
+ @_ == 1 or confess 0+@_;
+ my($code) = @_;
+ ref($code) eq 'CODE' or confess 'oops';
+ my $k = &$code($io->getreadbuf);
+ if (! defined($k)) {
+ $log->push($io);
+ return undef;
+ }
+ if ($k == 0) {
+ my $n = $io->readbuf;
+ defined($n) or
+ $log->push, return undef;
+ if ($n == 0) {
+ if ($io->getreadbuf eq "") {
+ return "";
+ }
+ $log->put("incomplete input: %s", $io->getreadbuf)->push($io);
+ return undef;
+ }
+ $k = &$code($io->getreadbuf);
+ if (! defined($k)) {
+ $log->push($io);
+ return undef;
+ }
+ if ($k == 0) {
+ return "";
+ }
+ }
+ my $head = substr($io->getreadbuf, 0, $k);
+ my $tail = substr($io->getreadbuf, $k);
+ $io->setreadbuf($tail)
+ or $log->push, return undef;
+ return $head;
+}
+
+sub readline {
+ my $io = shift;
+ @_ == 0 or confess 0+@_;
+ my $code = sub {
+ my $i = index($_[0], "\n");
+ return $i < 0 ? 0 : $i + 1;
+ };
+ return $io->readupto($code);
+}
+
+sub write {
+ my $io = shift;
+ @_ == 1 or confess 0+@_;
+ my($data) = @_;
+ my $timeout = $io->hastimeout ? $io->gettimeout : 0;
+ my $fh = $io->getfh;
+ (my $show = $data) =~ s!\n!\\n!g;
+ $log->put("write: $show")->push($io)->debug;
+ my $n;
+ my $size = length($data);
+ eval {
+ local $SIG{PIPE} = sub { die("broken pipe\n") };
+ if ($^O ne 'MSWin32' && $timeout > 0) {
+ local $SIG{ALRM} = sub { die("timed out\n") };
+ alarm($timeout);
+ $n = syswrite($fh, $data, $size);
+ alarm(0);
+ }
+ else {
+ $n = syswrite($fh, $data, $size);
+ }
+ };
+ if ($@) {
+ $log->put("write error: $@")->push($io);
+ return undef;
+ }
+ if (! defined($n)) {
+ $log->put("write failed: $!")->push($io);
+ return undef;
+ }
+ if ($n > $size) {
+ $log->put("impossible write: $n > $size")->push($io);
+ return undef;
+ }
+ if ($n != $size) { # need not be error
+ $log->put("short write: $n < $size")->push($io);
+ }
+ return $n;
+}
+
+sub close {
+ my $io = shift;
+ @_ == 0 or confess 0+@_;
+ if (! close($io->delfh)) {
+ $log->put("close failed: $!")->push($io);
+ return undef;
+ }
+ return 1;
+}
+
+1;
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Lock.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Lock.pm
new file mode 100644
index 00000000000..b515e633059
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Lock.pm
@@ -0,0 +1,136 @@
+package NDB::Util::Lock;
+
+use strict;
+use Carp;
+use Symbol;
+use Fcntl qw(:flock);
+use Errno;
+use File::Basename;
+
+require NDB::Util::File;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::File);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::Lock->attributes(
+ pid => sub { $_ != 0 },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $lock = $class->SUPER::new(%attr);
+ return $lock;
+}
+
+sub desc {
+ my $lock = shift;
+ return $lock->getpath;
+}
+
+# test / set
+
+sub test {
+ my $lock = shift;
+ @_ == 0 or confess 0+@_;
+ my $fh = gensym();
+ if (! open($fh, "+<$lock->{path}")) {
+ if ($! != Errno::ENOENT) {
+ $log->put("$lock->{path}: open failed: $!");
+ return undef;
+ }
+ return 0; # file does not exist
+ }
+ if (flock($fh, LOCK_EX|LOCK_NB)) {
+ close($fh);
+ return 0; # file was not locked
+ }
+ if ($^O eq 'MSWin32') {
+ close($fh);
+ if (! open($fh, "<$lock->{path}x")) {
+ $log->put("$lock->{path}x: open failed: $!");
+ return undef;
+ }
+ }
+ my $pid = <$fh>;
+ close($fh);
+ ($pid) = split(' ', $pid);
+ if ($pid+0 == 0) {
+ $log->put("$lock->{path}: locked but pid='$pid' is zero");
+ return undef;
+ }
+ $lock->{pid} = $pid;
+ return 1; # file was locked
+}
+
+sub set {
+ my $lock = shift;
+ @_ == 0 or confess 0+@_;
+ my $fh = gensym();
+ if (! open($fh, "+<$lock->{path}")) {
+ if ($! != Errno::ENOENT) {
+ $log->put("$lock->{path}: open failed: $!");
+ return undef;
+ }
+ close($fh);
+ if (! open($fh, ">$lock->{path}")) {
+ $log->put("$lock->{path}: create failed: $!");
+ return undef;
+ }
+ }
+ if (! flock($fh, LOCK_EX|LOCK_NB)) {
+ $log->put("$lock->{path}: flock failed: $!");
+ close($fh);
+ return 0; # file was probably locked
+ }
+ my $line = "$$\n";
+ if ($^O eq 'MSWin32') {
+ my $gh = gensym();
+ if (! open($gh, ">$lock->{path}x")) {
+ $log->put("$lock->{path}x: open for write failed: $!");
+ close($fh);
+ return undef;
+ }
+ if (! syswrite($gh, $line)) {
+ close($fh);
+ close($gh);
+ $log->put("$lock->{path}x: write failed: $!");
+ return undef;
+ }
+ close($gh);
+ } else {
+ if (! truncate($fh, 0)) {
+ close($fh);
+ $log->put("$lock->{path}: truncate failed: $!");
+ return undef;
+ }
+ if (! syswrite($fh, $line)) {
+ close($fh);
+ $log->put("$lock->{path}: write failed: $!");
+ return undef;
+ }
+ }
+ $lock->{fh} = $fh;
+ return 1; # file is now locked by us
+}
+
+sub close {
+ my $lock = shift;
+ @_ == 0 or confess 0+@_;
+ my $fh = delete $lock->{fh};
+ if ($fh) {
+ close($fh);
+ }
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Log.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Log.pm
new file mode 100644
index 00000000000..44b39df84e6
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Log.pm
@@ -0,0 +1,367 @@
+package NDB::Util::Log;
+
+use strict;
+use Carp;
+use Symbol;
+use Data::Dumper ();
+
+require NDB::Util::Base;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Base);
+
+# constructors
+
+my $instance = undef;
+my %attached = ();
+
+my %priolevel = qw(user 0 fatal 1 error 2 warn 3 notice 4 info 5 debug 6);
+my %partlist = qw(time 1 pid 2 prio 3 text 4 line 5);
+
+NDB::Util::Log->attributes(
+ prio => sub { defined($priolevel{$_}) },
+ parts => sub { ref eq 'HASH' },
+ stack => sub { ref eq 'ARRAY' },
+ io => sub { ref && $_->isa('NDB::Util::IO') },
+ active => sub { defined },
+ censor => sub { ref eq 'ARRAY' },
+);
+
+sub setpart {
+ my $log = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ while (@_) {
+ my $part = shift;
+ my $onoff = shift;
+ $partlist{$part} or confess 'oops';
+ $log->getparts->{$part} = $onoff;
+ }
+}
+
+sub getpart {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($part) = @_;
+ $partlist{$part} or confess 'oops';
+ return $log->getparts->{$part};
+}
+
+sub instance {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ if (! $instance) {
+ $instance = $class->SUPER::new(%attr);
+ $instance->setprio(q(info));
+ $instance->setparts({ text => 1 });
+ $instance->setstack([]);
+ $instance->setcensor([]);
+ my $io = NDB::Util::IO->new(fh => \*STDERR, %attr)
+ or confess 'oops';
+ $instance->setio($io);
+ }
+ return $instance;
+}
+
+# attached logs are written in parallel to main log
+# user log is a special server-to-client log
+
+sub attach {
+ my $log = shift;
+ @_ % 2 == 1 or confess 0+@_;
+ my($key, %attr) = @_;
+ $attached{$key} and confess 'oops';
+ my $alog = $attached{$key} = $log->clone(%attr);
+ return $alog;
+}
+
+sub detach {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($key) = @_;
+ $attached{$key} or return undef;
+ my $alog = delete $attached{$key};
+ return $alog;
+}
+
+sub attachuser {
+ my $log = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ %attr = (
+ prio => q(user),
+ parts => { text => 1 },
+ censor => [ qw(NDB::Net::Client NDB::Util::IO) ],
+ %attr);
+ my $alog = $log->attach(q(user), %attr);
+ return $alog;
+}
+
+sub detachuser {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ my $alog = $log->detach(q(user));
+ return $alog;
+}
+
+# input / output
+
+sub setfile {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my $file = shift;
+ if (! open(STDOUT, ">>$file")) {
+ $log->put("$file: open for append failed: $!");
+ return undef;
+ }
+ select(STDOUT);
+ $| = 1;
+ open(STDERR, ">&STDOUT");
+ select(STDERR);
+ $| = 1;
+ return 1;
+}
+
+sub close {
+ my $log = shift;
+ $log->getio->close;
+}
+
+sub closeall {
+ my $class = shift;
+ for my $key (sort keys %attached) {
+ my $log = $attached{$key};
+ $log->close;
+ }
+ $instance->close;
+}
+
+# private
+
+sub entry {
+ my $log = shift;
+ my($clear, $file, $line, @args) = @_;
+ $file =~ s!^.*\bNDB/!!;
+ $file =~ s!^.*/bin/([^/]+)$!$1!;
+ my $text = undef;
+ if (@args) {
+ $text = shift(@args);
+ if (! ref($text)) {
+ if (@args) {
+ $text = sprintf($text, @args);
+ }
+ while (chomp($text)) {}
+ }
+ }
+ if ($clear) {
+ $#{$log->getstack} = -1;
+ }
+ push(@{$log->getstack}, {
+ line => "$file($line)",
+ text => $text,
+ });
+}
+
+sub matchlevel {
+ my $log = shift;
+ my $msgprio = shift;
+ my $logprio = $log->getprio;
+ my $msglevel = $priolevel{$msgprio};
+ my $loglevel = $priolevel{$logprio};
+ defined($msglevel) && defined($loglevel)
+ or confess 'oops';
+ if ($msglevel == 0 && $loglevel == 0) {
+ return $msgprio eq $logprio;
+ }
+ if ($msglevel == 0 && $loglevel != 0) {
+ return $loglevel >= $priolevel{q(info)};
+ }
+ if ($msglevel != 0 && $loglevel == 0) {
+ return $msglevel <= $priolevel{q(notice)};
+ }
+ if ($msglevel != 0 && $loglevel != 0) {
+ return $msglevel <= $loglevel;
+ }
+ confess 'oops';
+}
+
+sub print {
+ my $log = shift;
+ @_ == 2 or confess 0+@_;
+ my($prio, $tmpstack) = @_;
+ if ($log->hasactive) { # avoid recursion
+ return;
+ }
+ if (! $log->matchlevel($prio)) {
+ return;
+ }
+ $log->setactive(1);
+ my @text = ();
+ if ($log->getpart(q(time))) {
+ my @t = localtime(time);
+ push(@text, sprintf("%02d-%02d/%02d:%02d:%02d",
+ 1+$t[4], $t[3], $t[2], $t[1], $t[0]));
+ }
+ if ($log->getpart(q(pid))) {
+ push(@text, "[$$]");
+ }
+ if ($log->getpart(q(prio)) &&
+ (0 == $priolevel{$prio} || $priolevel{$prio} <= $priolevel{notice}))
+ {
+ push(@text, "[$prio]");
+ }
+ if ($log->getpart(q(text))) {
+ my @stack = @$tmpstack;
+ while (@stack) {
+ my $s = pop(@stack);
+ my $text = $s->{text};
+ if (ref($text)) {
+ if (grep($text->isa($_), @{$log->getcensor})) {
+ next;
+ }
+ $text = $text->desc;
+ }
+ push(@text, $text) if length($text) > 0;
+ }
+ }
+ if ($log->getpart(q(line)) &&
+ (0 < $priolevel{$prio} && $priolevel{$prio} <= $priolevel{warn}))
+ {
+ push(@text, "at");
+ my @stack = @$tmpstack;
+ while (@stack) {
+ my $s = shift(@stack);
+ defined($s->{line}) or confess 'oops';
+ if ($text[-1] ne $s->{line}) {
+ push(@text, $s->{line});
+ }
+ }
+ }
+ $log->getio->write("@text\n");
+ $log->delactive;
+}
+
+sub printall {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($prio) = @_;
+ my $logstack = $log->getstack;
+ if (! @$logstack) {
+ $log->put("[missing log message]");
+ }
+ my @tmpstack = ();
+ while (@$logstack) {
+ push(@tmpstack, shift(@$logstack));
+ }
+ for my $key (sort keys %attached) {
+ my $alog = $attached{$key};
+ $alog->print($prio, \@tmpstack);
+ }
+ $instance->print($prio, \@tmpstack);
+}
+
+# public
+
+sub push {
+ my $log = shift;
+ my(@args) = @_;
+ my($pkg, $file, $line) = caller;
+ $log->entry(0, $file, $line, @args);
+ return $log;
+}
+
+sub put {
+ my $log = shift;
+ my(@args) = @_;
+ my($pkg, $file, $line) = caller;
+ $log->entry(1, $file, $line, @args);
+ return $log;
+}
+
+sub fatal {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(fatal));
+ exit(1);
+}
+
+sub error {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(error));
+ return $log;
+}
+
+sub warn {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(warn));
+ return $log;
+}
+
+sub notice {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(notice));
+ return $log;
+}
+
+sub info {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(info));
+ return $log;
+}
+
+sub debug {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(debug));
+ return $log;
+}
+
+sub user {
+ my $log = shift;
+ @_ == 0 or confess 0+@_;
+ $log->printall(q(user));
+ return $log;
+}
+
+# return values from server to client
+
+sub putvalue {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($value) = @_;
+ my $d = Data::Dumper->new([$value], [qw($value)]);
+ $d->Indent(0);
+ $d->Useqq(1);
+ my $dump = $d->Dump;
+ $dump =~ /^\s*\$value\s*=\s*(.*);\s*$/ or confess $dump;
+ $log->push("[value $1]");
+}
+
+sub hasvalue {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($line) = @_;
+ return $line =~ /\[value\s+(.*)\]/;
+}
+
+sub getvalue {
+ my $log = shift;
+ @_ == 1 or confess 0+@_;
+ my($line) = @_;
+ $line =~ /\[value\s+(.*)\]/ or confess $line;
+ my $expr = $1;
+ my($value);
+ eval "\$value = $expr";
+ if ($@) {
+ $log->put("$line: eval error: $@");
+ return undef;
+ }
+ return [$value];
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Socket.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Socket.pm
new file mode 100644
index 00000000000..00e8b6eca51
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Socket.pm
@@ -0,0 +1,158 @@
+package NDB::Util::Socket;
+
+use strict;
+use Carp;
+use Symbol;
+use Socket;
+use Errno;
+
+require NDB::Util::IO;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::IO);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::Socket->attributes(
+ domain => sub { $_ == PF_INET || $_ == PF_UNIX },
+ type => sub { $_ == SOCK_STREAM },
+ proto => sub { /^(0|tcp)$/ },
+);
+
+sub desc {
+ my $socket = shift;
+ return $socket->SUPER::desc;
+}
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $socket = $class->SUPER::new(%attr);
+ $socket->setdomain($attr{domain})
+ or $log->push, return undef;
+ $socket->settype($attr{type})
+ or $log->push, return undef;
+ $socket->setproto($attr{proto})
+ or $log->push, return undef;
+ my $nproto;
+ if ($socket->getproto =~ /^\d+/) {
+ $nproto = $socket->getproto;
+ }
+ else {
+ $nproto = getprotobyname($socket->getproto);
+ unless (defined($nproto)) {
+ $log->put("%s: getprotobyname failed", $socket->getproto);
+ return undef;
+ }
+ }
+ my $fh = gensym();
+ if (! socket($fh, $socket->getdomain, $socket->gettype, $nproto)) {
+ $log->put("create socket failed: $!");
+ return undef;
+ }
+ $socket->setfh($fh)
+ or $log->push, return undef;
+ return $socket;
+}
+
+sub setopt {
+ my $socket = shift;
+ @_ >= 2 or confess 'oops';
+ my $level = shift;
+ my $optname = shift;
+ my $optval = @_ ? pack("l*", @_) : undef;
+ my $fh = $socket->getfh;
+ if (! setsockopt($fh, $level, $optname, $optval)) {
+ $log->put("setsockopt failed: $!")->push($socket);
+ return undef;
+ }
+ return 1;
+}
+
+sub connect {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($paddr) = @_;
+ my $fh = $socket->getfh;
+ if (! connect($fh, $paddr)) {
+ $log->put("connect failed: $!")->push($socket);
+ return undef;
+ }
+ $log->put("connect done")->push($socket)->debug;
+ return 1;
+}
+
+sub bind {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($paddr) = @_;
+ my $fh = $socket->getfh;
+ if (! bind($fh, $paddr)) {
+ $log->put("bind failed: $!")->push($socket);
+ return undef;
+ }
+ return 1;
+}
+
+sub listen {
+ my $socket = shift;
+ @_ == 0 or confess 0+@_;
+ my $fh = $socket->getfh;
+ if (! listen($fh, SOMAXCONN)) {
+ $log->put("listen failed: $!")->push($socket);
+ return undef;
+ }
+ return 1;
+}
+
+sub accept {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($timeout) = @_;
+ $timeout =~ /^\d+$/ or confess 'oops';
+ my $fh = $socket->getfh;
+ my $gh = gensym();
+ my $paddr;
+ eval {
+ if ($^O ne 'MSWin32' && $timeout > 0) {
+ local $SIG{ALRM} = sub { die("timed out\n") };
+ alarm($timeout);
+ $paddr = accept($gh, $fh);
+ alarm(0);
+ }
+ else {
+ $paddr = accept($gh, $fh);
+ }
+ };
+ if ($@) {
+ $log->put("accept failed: $@")->push($socket);
+ return undef;
+ }
+ if (! $paddr) {
+ my $errno = 0+$!;
+ if ($errno == Errno::EINTR) {
+ $log->put("accept interrupted")->push($socket);
+ return 0;
+ }
+ $log->put("accept failed: $!")->push($socket);
+ return undef;
+ }
+ my $csocket = $socket->clone(fh => $gh);
+ $csocket->acceptaddr($paddr);
+ return $csocket;
+}
+
+sub DESTROY {
+ my $socket = shift;
+ $socket->close;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketINET.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketINET.pm
new file mode 100644
index 00000000000..faaa568a08e
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketINET.pm
@@ -0,0 +1,86 @@
+package NDB::Util::SocketINET;
+
+use strict;
+use Carp;
+use Symbol;
+use Socket;
+use Errno;
+
+require NDB::Util::Socket;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Socket);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::SocketINET->attributes(
+ host => sub { /^\S+$/ },
+ port => sub { /^\d+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $socket = $class->SUPER::new(%attr,
+ domain => PF_INET, type => SOCK_STREAM, proto => 'tcp')
+ or $log->push, return undef;
+ return $socket;
+}
+
+sub connect {
+ my $socket = shift;
+ @_ == 2 or confess 0+@_;
+ my($host, $port) = @_;
+ $port =~ /^\d+$/ or confess 'oops';
+ my $iaddr = inet_aton($host);
+ if (! $iaddr) {
+ $log->put("host $host not found")->push($socket);
+ return undef;
+ }
+ my $paddr = pack_sockaddr_in($port, $iaddr);
+ $socket->SUPER::connect($paddr)
+ or $log->push, return undef;
+ $socket->sethost($host)
+ or $log->push, return undef;
+ $socket->setport($port)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub bind {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($port) = @_;
+ $port =~ /^\d+$/ or confess 'oops';
+ my $paddr = pack_sockaddr_in($port, INADDR_ANY);
+ $socket->SUPER::bind($paddr)
+ or $log->push, return undef;
+ $socket->setport($port)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub acceptaddr {
+ my $csocket = shift;
+ @_ == 1 or confess 0+@_;
+ my($paddr) = @_;
+ my($port, $iaddr) = unpack_sockaddr_in($paddr);
+ my $host = gethostbyaddr($iaddr, AF_INET);
+ $csocket->sethost($host)
+ or $log->push, return undef;
+ $csocket->setport($port)
+ or $log->push, return undef;
+ $log->put("accept: host=%s port=%d",
+ $csocket->gethost, $csocket->getport)->push($csocket)->debug;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketUNIX.pm b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketUNIX.pm
new file mode 100644
index 00000000000..9c6b3115f6a
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketUNIX.pm
@@ -0,0 +1,76 @@
+package NDB::Util::SocketUNIX;
+
+use strict;
+use Carp;
+use Symbol;
+use Socket;
+use Errno;
+
+require NDB::Util::Socket;
+
+use vars qw(@ISA);
+@ISA = qw(NDB::Util::Socket);
+
+# constructors
+
+my $log;
+
+sub initmodule {
+ $log = NDB::Util::Log->instance;
+}
+
+NDB::Util::SocketUNIX->attributes(
+ path => sub { /^\S+$/ },
+);
+
+sub new {
+ my $class = shift;
+ @_ % 2 == 0 or confess 0+@_;
+ my(%attr) = @_;
+ my $socket = $class->SUPER::new(%attr,
+ domain => PF_UNIX, type => SOCK_STREAM, proto => 0)
+ or $log->push, return undef;
+ return $socket;
+}
+
+sub connect {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($path) = @_;
+ $path =~ /^\S+$/ or confess 'oops';
+ my $paddr = pack_sockaddr_un($path);
+ $socket->SUPER::connect($paddr)
+ or $log->push, return undef;
+ $socket->setpath($path)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub bind {
+ my $socket = shift;
+ @_ == 1 or confess 0+@_;
+ my($path) = @_;
+ $path =~ /^\S+$/ or confess 'oops';
+ my $paddr = pack_sockaddr_un($path);
+ $socket->SUPER::bind($paddr)
+ or $log->push, return undef;
+ $socket->setpath($path)
+ or $log->push, return undef;
+ return 1;
+}
+
+sub acceptaddr {
+ my $csocket = shift;
+ @_ == 1 or confess 0+@_;
+ my($paddr) = @_;
+ return 1; # crash
+ my $path = unpack_sockaddr_un($paddr);
+ $csocket->setpath($path)
+ or $log->push, return undef;
+ $log->put("%s accept: path=%s",
+ $csocket->getpath)->push($csocket)->debug;
+ return 1;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/ndbnet.pl b/storage/ndb/tools/old_dirs/ndbnet/ndbnet.pl
new file mode 100644
index 00000000000..5f6648da46d
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/ndbnet.pl
@@ -0,0 +1,339 @@
+#! /usr/local/bin/perl
+
+use strict;
+use POSIX();
+use Socket;
+use Getopt::Long;
+use File::Basename;
+use Term::ReadLine;
+
+use NDB::Net;
+
+select(STDOUT);
+$| = 1;
+
+# get options and environment
+
+my $log = NDB::Util::Log->instance;
+$log->setpart();
+
+sub printhelp {
+ print <<END;
+ndbnet -- ndbnet client
+usage: ndbnet [options] [command...]
+--help print this text and exit
+--base dir ndb installation, default \$NDB_BASE
+--netcfg file net config, default \$NDB_BASE/etc/ndbnet.xml
+--server id ndbnetd server id, or host:port if no config
+--noterm no prompting and no input editing
+--log prio debug/info/notice/warn/error/fatal, default info
+command... command (by default becomes interactive)
+END
+ exit(0);
+}
+
+my $progopts = {};
+my @progargv;
+
+anon: {
+ local $SIG{__WARN__} = sub {
+ my $errstr = "@_";
+ while (chomp($errstr)) {}
+ $log->put("$errstr (try --help)")->fatal;
+ };
+ Getopt::Long::Configure(qw(
+ default no_getopt_compat no_ignore_case require_order
+ ));
+ GetOptions($progopts, qw(
+ help base=s netcfg=s server=s noterm log=s
+ ));
+}
+
+$progopts->{help} && printhelp();
+if (defined(my $prio = $progopts->{log})) {
+ $log->setprio($prio);
+}
+@progargv = @ARGV;
+
+my $netenv = NDB::Net::Env->instance(
+ base => $progopts->{base},
+ netcfg => $progopts->{netcfg},
+);
+$netenv or $log->fatal;
+
+# get servers from command line or from net config
+
+my @servers = ();
+my $netcfg;
+if ($netenv->hasnetcfg) {
+ $netcfg = NDB::Net::Config->new(file => $netenv->getnetcfg);
+}
+
+if (defined(my $id = $progopts->{server})) {
+ if ($id !~ /:/) {
+ $netcfg or $log->put("need net config to find server $id")->fatal;
+ $netcfg->load or $log->push->fatal;
+ $netcfg->getservers or $log->push->fatal;
+ my $s = NDB::Net::Server->get($id) or $log->fatal;
+ push(@servers, $s);
+ } else {
+ my($host, $port) = split(/:/, $id, 2);
+ my $s = NDB::Net::ServerINET->new(id => "?", host => $host, port => $port)
+ or $log->fatal;
+ push(@servers, $s);
+ }
+} else {
+ $netcfg or $log->put("need net config to find servers")->fatal;
+ $netcfg->load or $log->push->fatal;
+ my $list = $netcfg->getservers or $log->fatal;
+ @servers= @$list;
+ @servers or $log->put("no servers")->push($netcfg)->fatal;
+}
+
+# server commands
+
+my $server;
+sub doserver {
+ my($cmd) = @_;
+ my $ret;
+ my $found;
+ for my $s (@servers) {
+ if (! $s->testconnect) {
+ $log->warn;
+ next;
+ }
+ $found = 1;
+ if ($server ne $s) {
+ $server = $s;
+ $log->put("selected")->push($server)->debug;
+ }
+ $ret = $server->request($cmd);
+ last;
+ }
+ if (! $found) {
+ $log->put("no available server");
+ return undef;
+ }
+ my %seen = ();
+ @servers = grep(! $seen{$_}++, $server, @servers);
+ defined($ret) or $log->push, return undef;
+ return $ret;
+}
+
+# local commands
+
+sub cmd_help {
+ my($cmd) = @_;
+ my $text = $cmd->helptext;
+ defined($text) or return undef;
+ while(chomp($text)) {}
+ print $text, "\n";
+ return 1;
+}
+
+sub cmd_alias {
+ my($cmd) = @_;
+ my $text = $cmd->aliastext;
+ while(chomp($text)) {}
+ print $text, "\n";
+}
+
+sub cmd_quit {
+ my($cmd) = @_;
+ $log->put("bye-bye")->info;
+ exit(0);
+}
+
+sub cmd_server {
+ my($cmd) = @_;
+ my $action = $cmd->getarg(0);
+ if ($action !~ /^(start|restart|stop|ping)$/) {
+ $log->put("$action: undefined action");
+ return undef;
+ }
+ if ($action eq 'start') {
+ $cmd->setopt('direct')
+ or $log->push, return undef;
+ }
+ if ($action eq 'ping' && ! @{$cmd->getarglist(1)}) {
+ $cmd->setopt('all')
+ or $log->push, return undef;
+ }
+ if (! $cmd->getopt('direct')) {
+ return doserver($cmd);
+ }
+ $netcfg->load
+ or return undef;
+ my $servers = $netcfg->getservers
+ or return undef;
+ my $list;
+ if ($cmd->getopt('all')) {
+ $list = $servers;
+ }
+ else {
+ $list = [];
+ for my $id (@{$cmd->getarglist(1)}) {
+ if (my $s = NDB::Net::ServerINET->get($id)) {
+ push(@$list, $s);
+ next;
+ }
+ if (my $s = NDB::Net::ServerINET->match($id, undef, $servers)) {
+ if (@$s) {
+ push(@$list, @$s);
+ next;
+ }
+ }
+ $log->push;
+ return undef;
+ }
+ }
+ if (! @$list) {
+ $log->put("no servers specified, use --all for all")->info;
+ return 1;
+ }
+ for my $s (@$list) {
+ if ($action eq 'ping') {
+ if ($s->testconnect) {
+ $log->put("is alive")->push($s);
+ }
+ $log->info;
+ next;
+ }
+ if ($action eq 'start') {
+ if ($s->testconnect) {
+ $log->put("already running")->push($s)->info;
+ next;
+ }
+ }
+ my $script = $cmd->getopt('script') || "ndbnetd";
+ my @cmd = ($script);
+ if ($action eq 'restart') {
+ push(@cmd, "--restart");
+ }
+ if ($action eq 'stop') {
+ push(@cmd, "--stop");
+ }
+ if ($cmd->getopt('pass')) {
+ my $base = $netenv->getbase;
+ $cmd[0] = "$base/bin/$cmd[0]";
+ }
+ if ($cmd->getopt('parallel')) {
+ my $pid = fork;
+ defined($pid) or
+ $log->push("fork failed: $!"), return undef;
+ $pid > 0 && next;
+ }
+ $log->put("$action via ssh")->push($s->getcanon)->push($s)->info;
+ $log->put("run: @cmd")->push($s)->debug;
+ system 'ssh', '-n', $s->getcanon, "@cmd";
+ if ($cmd->getopt('parallel')) {
+ exit(0);
+ }
+ }
+ if ($cmd->getopt('parallel')) {
+ while ((my $pid = waitpid(-1, &POSIX::WNOHANG)) > 0) {
+ ;
+ }
+ }
+ return 1;
+}
+
+sub cmd_list {
+ my($cmd) = @_;
+ my $ret = doserver($cmd) or
+ $log->push, return undef;
+ my @out = ();
+ my @o = qw(NAME NODES PROCESS STATUS COMMENT);
+ push(@out, [ @o ]);
+ for my $name (sort keys %$ret) {
+ $#o = -1;
+ $o[0] = $name;
+ my $dbsts = $ret->{$name};
+ my @tmp = sort { $a->{id} <=> $b->{id} } values %{$dbsts->{node}};
+ my @nodesmgmt = grep($_->{type} eq 'mgmt', @tmp);
+ my @nodesdb = grep($_->{type} eq 'db', @tmp);
+ my @nodesapi = grep($_->{type} eq 'api', @tmp);
+ my @nodes = (@nodesmgmt, @nodesdb, @nodesapi);
+ $o[1] = sprintf("%d/%d/%d", 0+@nodesmgmt, 0+@nodesdb, 0+@nodesapi);
+ $o[2] = "-";
+ $o[3] = "-";
+ $o[4] = $dbsts->{comment};
+ $o[4] .= " - " if length $o[4];
+ $o[4] .= basename($dbsts->{home});
+ push(@out, [ @o ]);
+ for my $nodests (@nodes) {
+ $#o = -1;
+ $o[0] = $nodests->{id} . "-" . $nodests->{type};
+ $o[1] = $nodests->{host};
+ $o[1] =~ s/\..*//;
+ $o[2] = $nodests->{run};
+ $o[3] = $nodests->{status} || "-";
+ $o[4] = $nodests->{comment} || "-";
+ push(@out, [ @o ]);
+ }
+ }
+ my @len = ( 8, 8, 8, 8 );
+ for my $o (@out) {
+ for my $i (0..$#len) {
+ $len[$i] = length($o->[$i]) if $len[$i] < length($o->[$i]);
+ }
+ }
+ for my $o (@out) {
+ my @t = ();
+ for my $i (0..$#{$out[0]}) {
+ my $f = $len[$i] ? "%-$len[$i].$len[$i]s" : "%s";
+ push(@t, sprintf($f, $o->[$i]));
+ }
+ print "@t\n";
+ }
+ return 1;
+}
+
+# main program
+
+sub docmd {
+ my(@args) = @_;
+ my $cmd = NDB::Net::Command->new(@args)
+ or return undef;
+ my $name = $cmd->getname;
+ my $doit;
+ {
+ no strict 'refs';
+ $doit = *{"cmd_$name"};
+ }
+ if (! defined(&$doit)) {
+ $doit = \&doserver;
+ }
+ my $ret = &$doit($cmd);
+ defined($ret) or $log->push, return undef;
+ return $ret;
+}
+
+if (@progargv) {
+ docmd(argv => \@progargv) or $log->push->fatal;
+ exit(0);
+}
+
+my $term;
+if ((-t STDIN) && (-t STDOUT) && ! $progopts->{noterm}) {
+ $term = Term::ReadLine->new("ndbnet");
+ $term->ornaments(0);
+}
+
+print "type 'h' for help\n" if $term;
+while (1) {
+ my($line);
+ while (! $line) {
+ $line = $term ? $term->readline("> ") : <STDIN>;
+ if (! defined($line)) {
+ print("\n") if $term;
+ $line = 'EOF';
+ }
+ }
+ my $ret = docmd(line => $line);
+ $ret or $log->error;
+ ($line eq 'EOF') && last;
+}
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/ndbnetd.pl b/storage/ndb/tools/old_dirs/ndbnet/ndbnetd.pl
new file mode 100644
index 00000000000..95fa5322abc
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/ndbnetd.pl
@@ -0,0 +1,400 @@
+#! /usr/local/bin/perl
+
+use strict;
+use POSIX();
+use Socket;
+use Getopt::Long;
+use File::Basename;
+use File::Spec;
+
+use NDB::Net;
+
+# save argv for restart via client
+my @origargv = @ARGV;
+
+# get options and environment
+
+my $log = NDB::Util::Log->instance;
+
+sub printhelp {
+ print <<END;
+ndbnetd -- ndbnet daemon
+usage: ndbnetd [options]
+--help print this text and exit
+--base dir ndb installation, default \$NDB_BASE
+--netcfg file net config, default \$NDB_BASE/etc/ndbnet.xml
+--port num port number (if more than 1 server on this host)
+--stop kill any existing server
+--restart kill any existing server and start a new one
+--fg run in foreground (test option)
+--log prio debug/info/notice/warn/error/fatal, default info
+END
+ exit(0);
+}
+
+my $progopts = {};
+anon: {
+ local $SIG{__WARN__} = sub {
+ my $errstr = "@_";
+ while (chomp($errstr)) {}
+ $log->put("$errstr (try --help)")->fatal;
+ };
+ Getopt::Long::Configure(qw(
+ default no_getopt_compat no_ignore_case no_require_order
+ ));
+ GetOptions($progopts, qw(
+ help base=s netcfg=s port=i stop restart fg log=s
+ ));
+}
+$progopts->{help} && printhelp();
+if (defined(my $prio = $progopts->{log})) {
+ $log->setprio($prio);
+}
+@ARGV and $log->put("extra args on command line")->fatal;
+
+my $netenv = NDB::Net::Env->instance(
+ base => $progopts->{base},
+ netcfg => $progopts->{netcfg},
+);
+$netenv or $log->fatal;
+$netenv->hasbase or $log->put("need NDB_BASE")->fatal;
+
+# load net config and find our entry
+
+my $netcfg = NDB::Net::Config->new(file => $netenv->getnetcfg)
+ or $log->push->fatal;
+my $server;
+
+sub loadnetcfg {
+ $netcfg->load or $log->push->fatal;
+ my $servers = $netcfg->getservers or $log->fatal;
+ my $host = $netenv->gethostname;
+ my $port = $progopts->{port} || 0;
+ my $list = NDB::Net::ServerINET->match($host, $port, $servers)
+ or $log->push->fatal;
+ @$list == 1
+ or $log->push->fatal;
+ $server = $list->[0];
+ $server->setlocal;
+}
+loadnetcfg();
+$log->put("this server")->push($server)->debug;
+
+# check if server already running
+
+my $lock;
+anon: {
+ my $dir = NDB::Util::Dir->new(path => File::Spec->catfile($netenv->getbase, "run"));
+ $dir->mkdir or $log->fatal;
+ my $name = sprintf("ndbnet%s.pid", $server->getid);
+ $lock = $dir->getfile($name)->getlock;
+ my $ret;
+ $ret = $lock->test;
+ defined($ret) or $log->fatal;
+ if ($ret) {
+ if ($progopts->{stop} || $progopts->{restart}) {
+ $log->put("stopping server %s pid=%s", $netenv->gethostname, $lock->getpid)->info;
+ if ($^O ne 'MSWin32') {
+ kill -15, $lock->getpid;
+ } else {
+ kill 15, $lock->getpid;
+ }
+ while (1) {
+ sleep 1;
+ $ret = $lock->test;
+ defined($ret) or $log->fatal;
+ if ($ret) {
+ if (! kill(0, $lock->getpid) && $! == Errno::ESRCH) {
+ $log->put("locked but gone (linux bug?)")->info;
+ $lock->unlink;
+ $ret = 0;
+ }
+ }
+ if (! $ret) {
+ if ($progopts->{stop}) {
+ $log->put("stopped")->info;
+ exit(0);
+ }
+ $log->put("restarting server %s", $netenv->gethostname)->info;
+ last;
+ }
+ }
+ }
+ else {
+ $log->put("already running pid=%s", $lock->getpid)->fatal;
+ }
+ }
+ else {
+ if ($progopts->{stop}) {
+ $log->put("not running")->info;
+ exit(0);
+ }
+ }
+ $lock->set or $log->fatal;
+}
+
+# become daemon, re-obtain the lock, direct log to file
+
+anon: {
+ $log->setpart(time => 1, pid => 1, prio => 1, line => 1);
+ $progopts->{fg} && last anon;
+ $lock->close;
+ my $dir = NDB::Util::Dir->new(path => $netenv->getbase . "/log");
+ $dir->mkdir or $log->fatal;
+ my $pid = fork();
+ defined($pid) or $log->put("fork failed: $!")->fatal;
+ if ($pid) {
+ exit(0);
+ }
+ $lock->set or $log->fatal;
+ if ($^O ne 'MSWin32') {
+ POSIX::setsid() or $log->put("setsid failed: $!")->fatal;
+ }
+ open(STDIN, "</dev/null");
+ my $name = sprintf("ndbnet%s.log", $server->getid);
+ $log->setfile($dir->getfile($name)->getpath) or $log->fatal;
+}
+$log->put("ndbnetd started pid=$$ port=%s", $server->getport)->info;
+
+# create server socket and event
+
+my $socket = NDB::Util::SocketINET->new or $log->fatal;
+my $event = NDB::Util::Event->new;
+
+# commands
+
+sub cmd_server_fg {
+ my($cmd) = @_;
+ my $action = $cmd->getarg(0);
+ if (! $cmd->getopt('local')) {
+ return 1;
+ }
+ if ($action eq 'restart') {
+ my $prog = $netenv->getbase . "/bin/ndbnetd";
+ my @argv = @origargv;
+ if (! grep(/^--restart$/, @argv)) {
+ push(@argv, "--restart");
+ }
+ unshift(@argv, basename($prog));
+ $lock->close;
+ $socket->close;
+ $log->put("restart: @argv")->push($server)->user;
+ $log->put("server restart")->putvalue(1)->user;
+ exec $prog @argv;
+ die "restart failed: $!";
+ }
+ if ($action eq 'stop') {
+ $log->put("stop by request")->push($server)->user;
+ $log->put("server stop")->putvalue(1)->user;
+ exit(0);
+ }
+ if ($action eq 'ping') {
+ return 1;
+ }
+ $log->put("$action: unimplemented");
+ return undef;
+}
+
+sub cmd_server_bg {
+ my($cmd) = @_;
+ loadnetcfg() or return undef;
+ my $action = $cmd->getarg(0);
+ if (! $cmd->getopt('local')) {
+ $cmd->setopt('local')
+ or $log->push, return undef;
+ my $servers = $netcfg->getservers or $log->fatal;
+ my $list;
+ if ($cmd->getopt('all')) {
+ $list = $servers;
+ }
+ else {
+ $list = [];
+ for my $id (@{$cmd->getarglist(1)}) {
+ if (my $s = NDB::Net::ServerINET->get($id)) {
+ push(@$list, $s);
+ next;
+ }
+ if (my $s = NDB::Net::ServerINET->match($id, undef, $servers)) {
+ if (@$s) {
+ push(@$list, @$s);
+ next;
+ }
+ }
+ $log->push;
+ return undef;
+ }
+ }
+ my $fail = 0;
+ for my $s (@$list) {
+ if (! $s->request($cmd)) {
+ $log->push->user;
+ $fail++;
+ }
+ }
+ if ($fail) {
+ $log->put("failed %d/%d", $fail, scalar(@$list));
+ return undef;
+ }
+ return 1;
+ }
+ if ($action eq 'restart') {
+ return 1;
+ }
+ if ($action eq 'stop') {
+ return 1;
+ }
+ if ($action eq 'ping') {
+ $log->put("is alive")->push($server)->user;
+ return 1;
+ }
+ $log->put("$action: unimplemented");
+ return undef;
+}
+
+sub cmd_start_bg {
+ my($cmd) = @_;
+ loadnetcfg() or return undef;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ $db->start($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_startnode_bg {
+ my($cmd) = @_;
+ loadnetcfg() or return undef;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ my $node = $db->getnode($cmd->getarg(1)) or return undef;
+ $node->start($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_stop_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ $db->stop($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_stopnode_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ my $node = $db->getnode($cmd->getarg(1)) or return undef;
+ $node->stop($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_kill_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ $db->kill($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_killnode_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ my $node = $db->getnode($cmd->getarg(1)) or return undef;
+ $node->kill($cmd->getopts) or return undef;
+ return 1;
+}
+
+sub cmd_statnode_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ my $node = $db->getnode($cmd->getarg(1)) or return undef;
+ my $ret = $node->stat($cmd->getopts) or return undef;
+ return $ret;
+}
+
+sub cmd_list_bg {
+ my($cmd) = @_;
+ loadnetcfg() or return undef;
+ my $dblist;
+ if ($cmd->getarg(0)) {
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ $dblist = [ $db ];
+ } else {
+ $dblist = $netcfg->getdatabases or return undef;
+ }
+ my $ret = {};
+ for my $db (@$dblist) {
+ my $status = $db->list($cmd->getopts) || "error";
+ $ret->{$db->getname} = $status;
+ }
+ return $ret;
+}
+
+sub cmd_writenode_bg {
+ my($cmd) = @_;
+ my $db = $netcfg->getdatabase($cmd->getarg(0)) or return undef;
+ my $node = $db->getnode($cmd->getarg(1)) or return undef;
+ my $ret = $node->write($cmd->getopts, $cmd->getarg(2)) or return undef;
+ return $ret;
+}
+
+# main program
+
+sub checkchild {
+ while ((my $pid = waitpid(-1, &POSIX::WNOHANG)) > 0) {
+ $log->put("harvested pid=$pid")->info;
+ }
+}
+
+my $gotterm = 0;
+$SIG{INT} = sub { $gotterm = 1 };
+$SIG{TERM} = sub { $gotterm = 1 };
+
+$socket->setopt(SOL_SOCKET, SO_REUSEADDR, 1) or $log->fatal;
+$socket->bind($server->getport) or $log->fatal;
+$socket->listen or $log->fatal;
+$event->set($socket, 'r');
+
+loop: {
+ try: {
+ my $n = $event->poll(10);
+ if ($gotterm) {
+ $log->put("terminate on signal")->info;
+ last try;
+ }
+ if (! defined($n)) {
+ $log->error;
+ sleep 1;
+ last try;
+ }
+ if (! $n) {
+ $log->debug;
+ last try;
+ }
+ if (! $event->test($socket, 'r')) {
+ last try;
+ }
+ my $csocket = $socket->accept(10);
+ if (! defined($csocket)) {
+ $log->error;
+ last try;
+ }
+ if (! $csocket) {
+ $log->warn;
+ last try;
+ }
+ my $client = NDB::Net::Client->new(
+ socket => $csocket,
+ serversocket => $socket,
+ serverlock => $lock,
+ event => $event,
+ context => 'main',
+ );
+ $client or $log->fatal;
+ }
+ loadnetcfg() or $log->fatal;
+ NDB::Net::Client->processall;
+ if ($gotterm) {
+ last loop;
+ }
+ redo loop;
+}
+
+$log->put("ndbnetd done")->info;
+
+1;
+# vim:set sw=4:
diff --git a/storage/ndb/tools/old_dirs/ndbnet/ndbrun b/storage/ndb/tools/old_dirs/ndbnet/ndbrun
new file mode 100644
index 00000000000..99121276d99
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbnet/ndbrun
@@ -0,0 +1,33 @@
+#! /bin/sh
+
+# just for autotest for now
+
+case $# in
+1) script=$1 ;;
+*) echo "usage: $0 script"; exit 1 ;;
+esac
+
+case $NDB_TOP in
+/*) ;;
+*) echo "$0: NDB_TOP not defined" >&2; exit 1 ;;
+esac
+
+case $script in
+/*) ;;
+*) for d in $NDB_TOP $NDB_TOP/test $NDB_TOP/test/ndbnet; do
+ if [ -f $d/$script ]; then
+ script=$d/$script
+ break
+ fi
+ done ;;
+esac
+
+if [ ! -f $script ]; then
+ echo "$0: $script: script not found" >&2; exit 1
+fi
+
+PERL5LIB=$NDB_TOP/lib/perl5:$PERL5LIB; export PERL5LIB
+
+perl -cw $script || exit 1
+perl $script
+exit $?
diff --git a/storage/ndb/tools/old_dirs/ndbsql/Makefile b/storage/ndb/tools/old_dirs/ndbsql/Makefile
new file mode 100644
index 00000000000..81ca87b0414
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/ndbsql/Makefile
@@ -0,0 +1,44 @@
+include .defs.mk
+
+TYPE := util
+
+BIN_TARGET := ndbsql
+
+#
+# If BIN_TARGET_LIBS include NDB_ODBC then the ODBC lib is
+# linked into the program and the user does not need to
+# set up any ODBC stuff to make it work.
+#
+# If you want to use this program together with some
+# other DBMS (e.g. MySQL or Oracle), then comment the line below.
+#
+BIN_TARGET_LIBS = NDB_ODBC
+
+#BIN_TARGET_ARCHIVES := mgmapi NDB_API
+
+ifneq ($(USE_EDITLINE), N)
+BIN_TARGET_ARCHIVES += editline
+#DIRS := mkconfig
+endif
+
+BIN_FLAGS += $(TERMCAP_LIB)
+
+#ifneq ($(USE_TERMCAP), N)
+#LDFLAGS_LOC = -ltermcap
+#endif
+
+
+# Source files of non-templated classes (.cpp files)
+SOURCES = \
+ ndbsql.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/src/ndbapi) \
+ -I$(call fixpath,$(NDB_TOP)/include/mgmapi) \
+ -I$(call fixpath,$(NDB_TOP)/include/util) \
+ -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
+
+
+include $(NDB_TOP)/Epilogue.mk
+
+_bins_mkconfig : $(NDB_TOP)/bin/$(BIN_TARGET)
+
diff --git a/storage/ndb/tools/old_dirs/select_all/Makefile b/storage/ndb/tools/old_dirs/select_all/Makefile
new file mode 100644
index 00000000000..e14e411b3a5
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/select_all/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := select_all
+
+SOURCES := select_all.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/select_count/Makefile b/storage/ndb/tools/old_dirs/select_count/Makefile
new file mode 100644
index 00000000000..35a53c6b046
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/select_count/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := select_count
+
+SOURCES := select_count.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/tools/old_dirs/src/counterviewer/CounterViewer.java b/storage/ndb/tools/old_dirs/src/counterviewer/CounterViewer.java
new file mode 100644
index 00000000000..317c1c75e28
--- /dev/null
+++ b/storage/ndb/tools/old_dirs/src/counterviewer/CounterViewer.java
@@ -0,0 +1,725 @@
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import javax.swing.*;
+
+class Node extends Observable {
+ public final static int UNDEFINED = -1;
+ public final static int NDB_NODE = 0;
+ public final static int MGM_NODE = 1;
+ public final static int API_NODE = 2;
+
+ public int getNodeType() { return m_nodeType;}
+ public static int getNodeType(String str) {
+ if(str.equals("NDB"))
+ return NDB_NODE;
+ if(str.equals("API"))
+ return API_NODE;
+ if(str.equals("MGM"))
+ return MGM_NODE;
+ return UNDEFINED;
+ }
+
+ protected int m_nodeType;
+}
+
+class NdbNode extends Node {
+ public NdbNode(){
+ m_nodeType = NDB_NODE;
+ }
+
+ public Counters getCounters() { return counters; }
+
+ public void setCounters(Counters _c) {
+
+ if(_c == null){
+ counters = null;
+ setChanged();
+ notifyObservers();
+ return;
+ }
+
+ int old_tps = 0;
+ int old_ops = 0;
+ int old_aps = 0;
+ int diff = 5;
+ if(counters != null){
+ old_tps = counters.tps;
+ old_ops = counters.ops;
+ old_aps = counters.aps;
+ diff = 5; //_c.epochsecs - counters.epochsecs;
+ }
+
+ switch(_c.type){
+ case Counters.TRANSACTIONS:
+ _c.tps = (_c.transactions -_c.aborts)/ diff;
+ _c.aps = _c.aborts / diff;
+ _c.ops = old_ops;
+ break;
+ case Counters.OPERATIONS:
+ _c.tps = old_tps;
+ _c.aps = old_aps;
+ _c.ops = _c.operations / diff;
+ break;
+ }
+
+ counters = _c;
+ setChanged();
+ notifyObservers();
+ }
+
+ public int getNodeState(){
+ return nodeState;
+ }
+
+ public static int getNodeState(String state){
+ if(state.equals("NOT_STARTED") ||
+ state.equals("NO_CONTACT"))
+ return 0;
+ return 1;
+ }
+
+ public void setState(int nodeState){
+ this.nodeState = nodeState;
+ if(nodeState == 0)
+ counters = null;
+ }
+
+ private int nodeState;
+ private Counters counters;
+}
+
+class MgmNode extends Node { public MgmNode(){ m_nodeType = MGM_NODE; } }
+class ApiNode extends Node { public ApiNode(){ m_nodeType = API_NODE; } }
+
+class Counters {
+
+ public static final int TRANSACTIONS = 0;
+ public static final int OPERATIONS = 1;
+
+ public Counters(){
+ transactions = operations = -1;
+ }
+
+ public int type;
+ public int transactions;
+ public int operations;
+ public int aborts;
+ public int tps;
+ public int ops;
+ public int aps;
+ public int epochsecs;
+
+ public String toString(){
+ return "[Counters"+
+ " transactions="+transactions+
+ " operations="+operations+
+ " tps="+tps+
+ " ops="+ops+
+ " ]";
+ }
+}
+
+class NdbCluster extends Observable {
+
+ NdbCluster(int node_types[], int num_nodes, int maxNodeId){
+
+ nodes = new Node[maxNodeId+1];
+ maxCounters = new Counters();
+
+ for(int i = 0; i<maxNodeId; i++)
+ nodes[i] = null;
+
+ for(int i = 1; i<num_nodes; i++)
+ switch(node_types[i]){
+ case Node.NDB_NODE:
+ nodes[i] = new NdbNode();
+ break;
+ case Node.API_NODE:
+ nodes[i] = new ApiNode();
+ break;
+ case Node.MGM_NODE:
+ nodes[i] = new MgmNode();
+ default:
+ }
+ }
+
+ public int getNoOfNdbNodes(){
+ if(nodes == null)
+ return 0;
+ int retVal = 0;
+ for(int i = 1; i<nodes.length; i++)
+ if(getNodeType(i) == Node.NDB_NODE)
+ retVal++;
+ return retVal;
+ }
+
+ public int getNodeType(int nodeId){
+ if(nodes == null || nodeId > nodes.length || nodes[nodeId] == null)
+ return Node.UNDEFINED;
+ return nodes[nodeId].getNodeType();
+ }
+
+ public Counters getCounters(int nodeId){
+ if(nodes == null || nodeId > nodes.length || nodes[nodeId] == null ||
+ nodes[nodeId].getNodeType() != Node.NDB_NODE)
+ return null;
+ return ((NdbNode)nodes[nodeId]).getCounters();
+ }
+
+ public void setCounters(int nodeId, Counters _c){
+ if(nodes == null || nodeId > nodes.length || nodes[nodeId] == null)
+ return;
+ ((NdbNode)nodes[nodeId]).setCounters(_c);
+
+ int maxSum = 0;
+ for(int i = 1; i<nodes.length; i++){
+ Counters c = getCounters(i);
+ if(c != null){
+ int sum = c.tps + c.ops + c.aps;
+ if(sum > maxSum){
+ maxCounters = c;
+ maxSum = sum;
+ }
+ }
+ }
+ setChanged();
+ notifyObservers();
+ }
+
+ public void setState(int nodeId, int nodeType, int nodeState){
+ if(nodes == null || nodeId > nodes.length || nodes[nodeId] == null ||
+ nodes[nodeId].getNodeType() != nodeType)
+ return;
+
+ if(nodeType != Node.NDB_NODE)
+ return;
+
+ ((NdbNode)nodes[nodeId]).setState(nodeState);
+ }
+
+ public void setNoConnection(){
+ for(int i = 1; i<nodes.length; i++){
+ Counters c = getCounters(i);
+ if(c != null){
+ setCounters(i, null);
+ }
+ }
+ }
+
+ public Counters getMaxCounters(){
+ return maxCounters;
+ }
+
+ private Node nodes[];
+ private Counters maxCounters;
+}
+
+class CountersPanel extends JPanel implements Observer
+{
+ public CountersPanel(Dimension dim, NdbCluster _cluster, int maxInRow)
+ {
+ cluster = _cluster;
+ cluster.addObserver(this);
+ maxRow = maxInRow;
+ reSize(dim);
+ }
+
+ private void showCounters(Graphics g, int node, int x, int y, boolean p)
+ {
+ Counters counters = cluster.getCounters(node);
+
+ if (counters == null || p){
+ // Mark processor as not available
+ g.setColor(Color.black);
+ g.fillRect(x, y, width, height);
+ } else {
+ int red = (counters.aps * height) / scale;
+ int green = (counters.tps * height) / scale;
+ int yellow = (counters.ops * height) / scale;
+
+ System.out.println("tps="+counters.tps+" ops="+counters.ops+" scale="+scale+" green="+green+" yellow="+yellow);
+
+ g.setColor(Color.white);
+ g.fillRect(x, y, width, height);
+ if (yellow + green + red <= height){ // Max 100% load
+ int yellow_scaled = yellow;
+ int green_scaled = green;
+ int red_scaled = red;
+ if (red_scaled > 0){
+ g.setColor(Color.red);
+ g.fillRect(x,
+ height + y - red_scaled,
+ width,
+ red_scaled);
+ }
+ if (green_scaled > 0){
+ g.setColor(Color.green);
+ g.fillRect(x,
+ height + y - red_scaled - green_scaled,
+ width,
+ green_scaled);
+ }
+ if (yellow_scaled > 0){
+ g.setColor(Color.yellow);
+ g.fillRect(x,
+ height + y - red_scaled - green_scaled - yellow_scaled,
+ width,
+ yellow_scaled);
+ }
+ }
+ // Draw box
+ g.setColor(Color.black);
+ g.drawRect(x, y, width, height);
+
+ float f = ((float)height)/((float)(lines+1));
+
+ for(int i = 0; i<lines; i++){
+ int ytmp = (int)(y+height-(i+1)*f);
+ g.drawLine(x, ytmp, x+width, ytmp);
+ }
+ }
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ Counters maxCounters = cluster.getMaxCounters();
+ final int sum = maxCounters.tps+maxCounters.ops+maxCounters.aps;
+ boolean skipDraw = false;
+ if(sum == 0){
+ skipDraw = true;
+ } else {
+ lines = (sum / 1000) + 1;
+ scale = (lines+1) * 1000;
+ }
+
+ int nodeId = 0;
+ int nodeNo = 0;
+ final int noOfNdbNodes = cluster.getNoOfNdbNodes();
+ for(int row = 0; row <= noOfNdbNodes / maxRow; row++) {
+ int end = Math.min(noOfNdbNodes, (row + 1) * maxRow);
+ for (int pos = 0; (nodeNo < noOfNdbNodes) && (pos < maxRow); pos++){
+ while(cluster.getNodeType(nodeId) != Node.NDB_NODE)
+ nodeId++;
+ showCounters(g,
+ nodeId,
+ xindent + (xgap + width) * pos,
+ yindent + row * (height + ygap),
+ skipDraw);
+ nodeNo++;
+ nodeId++;
+ }
+ }
+ }
+
+ public void setWidth(int maxInRow)
+ {
+ maxRow = maxInRow;
+ }
+
+ public void reSize(Dimension dim)
+ {
+ final int noOfNdbNodes = cluster.getNoOfNdbNodes();
+
+ // System.out.println(dim);
+ int noRows = (int) Math.ceil((double) noOfNdbNodes / (double) maxRow);
+ xgap = (noOfNdbNodes > 1) ? Math.max(2, dim.width / 50) : 0;
+ ygap = (noOfNdbNodes > 1) ? Math.max(2, dim.height / 20) : 0;
+ xindent = 10;
+ yindent = 10;
+ int heightOfScroll = 20;
+ Insets insets = getInsets();
+ width = (dim.width - (insets.left + insets.right) - 2*xindent + xgap)/maxRow - xgap;
+ height = (dim.height - (insets.top + insets.bottom) - 2*yindent + ygap - heightOfScroll)/noRows - ygap;
+ }
+
+
+ public void update(Observable o, Object arg){
+ repaint();
+ }
+
+ private int width, height, maxRow, xgap, ygap, xindent, yindent;
+ private int scale;
+ private int lines;
+ private NdbCluster cluster;
+}
+
+class CountersFrame extends JFrame
+ implements ComponentListener, AdjustmentListener
+{
+
+ public CountersFrame(NdbCluster cluster)
+ {
+ setTitle("CountersViewer");
+
+ final int noOfNdbNodes = cluster.getNoOfNdbNodes();
+
+ processorWidth = Math.min(noOfNdbNodes, 10);
+ setSize(Math.max(50, processorWidth*20),
+ Math.max(100, 200*noOfNdbNodes/processorWidth));
+ JPanel p = new JPanel();
+ addComponentListener(this);
+ p.addComponentListener(this);
+ getContentPane().add(p, "Center");
+ myPanel = new CountersPanel(getContentPane().getSize(),
+ cluster,
+ processorWidth);
+ getContentPane().add(myPanel, "Center");
+ JPanel labelAndScroll = new JPanel();
+ labelAndScroll.setLayout(new GridLayout (1, 2));
+ myWidthLabel = new JLabel("Width: " + processorWidth);
+ labelAndScroll.add(myWidthLabel);
+ myWidthScroll = new JScrollBar(Adjustable.HORIZONTAL,
+ Math.min(noOfNdbNodes, 10), 0, 1,
+ noOfNdbNodes);
+ myWidthScroll.addAdjustmentListener(this);
+ labelAndScroll.add(myWidthScroll);
+ if (noOfNdbNodes > 1)
+ getContentPane().add(labelAndScroll, "South");
+ }
+
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ }
+
+ public void componentResized(ComponentEvent e) {
+ myPanel.reSize(getContentPane().getSize());
+ repaint();
+ }
+
+ public void componentShown(ComponentEvent e) {
+ }
+
+ public void adjustmentValueChanged(AdjustmentEvent evt)
+ {
+ myPanel.setWidth(myWidthScroll.getValue());
+ myPanel.reSize(getContentPane().getSize());
+ myWidthLabel.setText("Width: " + myWidthScroll.getValue());
+ repaint();
+ }
+
+ private JScrollBar myWidthScroll;
+ private JLabel myWidthLabel;
+ private CountersPanel myPanel;
+ private int processorWidth = 10;
+}
+
+class CountersConnection {
+
+ public CountersConnection(String host, int port){
+ this.host = host;
+ this.port = port;
+ }
+
+ public boolean connect(){
+ if(br == null){
+ try {
+ InetAddress target = InetAddress.getByName(host);
+ sock = new Socket(target, port);
+
+ br = new BufferedReader(new InputStreamReader
+ (sock.getInputStream()));
+ } catch (Exception e){
+ System.out.println("connect: " + e);
+ }
+ }
+
+ if (br == null)
+ return false;
+ return true;
+ }
+
+ public void disconnect(){
+ try {
+ sock.close();
+ } catch (Exception e){
+ System.out.println("disconnect: " + e);
+ }
+ sock = null;
+ br = null;
+ }
+
+ public boolean readCounters(NdbCluster cluster) {
+ if(!connect()){
+ cluster.setNoConnection();
+ return false;
+ }
+ String str = null;
+
+ try {
+ str = br.readLine();
+ } catch (Exception e){
+ System.out.println("readLine: " + e);
+ }
+ if(str == null){
+ disconnect();
+ return false;
+ }
+ StringTokenizer st = new StringTokenizer(str, " ");
+
+ int nodeId = 0;
+ Counters c = new Counters();
+
+ while(st.hasMoreTokens()){
+ String tmp = st.nextToken();
+ int ind = 0;
+ if(tmp.startsWith("nodeid=")){
+ nodeId = Integer.valueOf(tmp.substring(7)).intValue();
+ } else if(tmp.startsWith("trans=")){
+ c.transactions = Integer.valueOf(tmp.substring(6)).intValue();
+ c.type = Counters.TRANSACTIONS;
+ } else if(tmp.startsWith("abort=")){
+ c.aborts = Integer.valueOf(tmp.substring(6)).intValue();
+ c.type = Counters.TRANSACTIONS;
+ } else if(tmp.startsWith("epochsecs=")){
+ c.epochsecs = Integer.valueOf(tmp.substring(11)).intValue();
+ } else if(tmp.startsWith("operations=")){
+ c.operations = Integer.valueOf(tmp.substring(11)).intValue();
+ c.type = Counters.OPERATIONS;
+ }
+ }
+
+ if(nodeId != 0)
+ cluster.setCounters(nodeId, c);
+
+ return true;
+ }
+
+ private Socket sock;
+ private BufferedReader br;
+ private String host;
+ private int port;
+}
+
+class MgmConnection {
+
+ public MgmConnection(String host, int port){
+ this.host = host;
+ this.port = port;
+ }
+
+ public NdbCluster getClusterInfo(){
+ NdbCluster cluster = null;
+ if(!connect())
+ return cluster;
+
+ out.println("get info cluster");
+ String str = null;
+ try {
+ str = br.readLine();
+ if(str.startsWith("GET INFO 0")){
+ StringTokenizer st = new StringTokenizer
+ (str.substring(11));
+ int nodes[] = new int[255];
+
+ int type = Node.UNDEFINED;
+ int num_nodes = 0;
+ int maxNodeId = 0;
+ while(st.hasMoreTokens()){
+ String tmp = st.nextToken();
+ final int t = Node.getNodeType(tmp);
+ if(t != Node.UNDEFINED)
+ type = t;
+
+ int nodeId = 0;
+ try {
+ nodeId = Integer.parseInt(tmp);
+ } catch (Exception e) {}
+ if(nodeId != 0){
+ num_nodes ++;
+ nodes[nodeId] = type;
+ if(nodeId > maxNodeId)
+ maxNodeId = nodeId;
+ }
+ }
+ cluster = new NdbCluster(nodes, num_nodes,
+ maxNodeId);
+ }
+
+ } catch(Exception e){
+ System.out.println("readLine: "+e);
+ }
+ return cluster;
+ }
+
+ public boolean connect(){
+ if(br == null || out == null){
+ try {
+ InetAddress target = InetAddress.getByName(host);
+ sock = new Socket(target, port);
+
+ br = new BufferedReader(new InputStreamReader
+ (sock.getInputStream()));
+ out = new PrintWriter(sock.getOutputStream(), true);
+ } catch (Exception e){
+ System.out.println("connect: " + e);
+ }
+ }
+
+ if (br == null || out == null)
+ return false;
+ return true;
+ }
+
+ public void disconnect(){
+ try {
+ sock.close();
+ } catch (Exception e){
+ System.out.println("disconnect: " + e);
+ }
+ sock = null;
+ br = null;
+ out = null;
+ }
+
+ public boolean readStatus(NdbCluster cluster){
+
+ if(!connect()){
+ cluster.setNoConnection();
+ return false;
+ }
+
+ String str = null;
+ try {
+ out.println("get status");
+ str = br.readLine();
+ } catch (Exception e){
+ System.out.println("readLine: " + e);
+ }
+ if(str == null){
+ disconnect();
+ return false;
+ }
+
+ if(!str.startsWith("GET STATUS")){
+ disconnect();
+ return false;
+ }
+
+ int nodes = 0;
+ try {
+ nodes = Integer.parseInt(str.substring(11));
+ } catch(Exception e){
+ System.out.println("parseInt "+e);
+ }
+ if(nodes == 0){
+ disconnect();
+ return false;
+ }
+
+ try {
+ for(; nodes > 0 ; nodes --){
+ str = br.readLine();
+ StringTokenizer st = new StringTokenizer(str);
+
+ String s_nodeId = st.nextToken();
+ final int nodeId = Integer.parseInt(s_nodeId);
+
+ String s_type = st.nextToken();
+ String s_state = st.nextToken();
+ String s_phase = st.nextToken();
+ int type = Node.getNodeType(s_type);
+ int state = NdbNode.getNodeState(s_state);
+
+ cluster.setState(nodeId, type, state);
+ }
+ } catch (Exception e){
+ disconnect();
+ return false;
+ }
+
+ return true;
+ }
+
+ public int getStatisticsPort(){
+ if(!connect())
+ return -1;
+
+ String str = null;
+ try {
+ out.println("stat port");
+ str = br.readLine();
+ } catch (Exception e){
+ System.out.println("readLine: " + e);
+ }
+ if(str == null){
+ disconnect();
+ return -1;
+ }
+
+ if(!str.startsWith("STAT PORT 0")){
+ disconnect();
+ return -1;
+ }
+
+ try {
+ return Integer.parseInt(str.substring(12));
+ } catch (Exception e){
+ System.out.println("parseInt "+e);
+ }
+ return -1;
+ }
+
+ private Socket sock;
+ private BufferedReader br;
+ private PrintWriter out;
+ private String host;
+ private int port;
+}
+
+class CounterViewer {
+
+ public static void usage(){
+ System.out.println("java CounterViewer <mgm host> <mgm port>");
+ }
+
+ public static void main(String args[]){
+ try {
+ String host = args[0];
+ int port = Integer.parseInt(args[1]);
+ new CounterViewer(host, port).run();
+ return;
+ } catch (Exception e){
+ }
+ usage();
+ }
+
+ MgmConnection mgmConnection;
+ CountersConnection countersConnection;
+
+ NdbCluster cluster;
+ boolean ok;
+
+ CounterViewer(String host, int port){
+ ok = false;
+
+ mgmConnection = new MgmConnection(host, port);
+ int statPort = mgmConnection.getStatisticsPort();
+ if(statPort < 0)
+ return;
+
+ countersConnection = new CountersConnection(host, statPort);
+ cluster = mgmConnection.getClusterInfo();
+
+ CountersFrame f = new CountersFrame(cluster);
+ f.setSize (300, 200);
+ f.show();
+
+ ok = true;
+ }
+
+ void run(){
+ while(ok){
+ mgmConnection.readStatus(cluster);
+ countersConnection.readCounters(cluster);
+ }
+ }
+}
+
diff --git a/storage/ndb/tools/restore/Restore.cpp b/storage/ndb/tools/restore/Restore.cpp
new file mode 100644
index 00000000000..fa616ee8fee
--- /dev/null
+++ b/storage/ndb/tools/restore/Restore.cpp
@@ -0,0 +1,947 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "Restore.hpp"
+#include <NdbTCP.h>
+#include <OutputStream.hpp>
+#include <Bitmask.hpp>
+
+#include <AttributeHeader.hpp>
+#include <trigger_definitions.h>
+#include <SimpleProperties.hpp>
+#include <signaldata/DictTabInfo.hpp>
+
+Uint16 Twiddle16(Uint16 in); // Byte shift 16-bit data
+Uint32 Twiddle32(Uint32 in); // Byte shift 32-bit data
+Uint64 Twiddle64(Uint64 in); // Byte shift 64-bit data
+
+bool
+BackupFile::Twiddle(const AttributeDesc* attr_desc, AttributeData* attr_data, Uint32 arraySize){
+ Uint32 i;
+
+ if(m_hostByteOrder)
+ return true;
+
+ if(arraySize == 0){
+ arraySize = attr_desc->arraySize;
+ }
+
+ switch(attr_desc->size){
+ case 8:
+
+ return true;
+ case 16:
+ for(i = 0; i<arraySize; i++){
+ attr_data->u_int16_value[i] = Twiddle16(attr_data->u_int16_value[i]);
+ }
+ return true;
+ case 32:
+ for(i = 0; i<arraySize; i++){
+ attr_data->u_int32_value[i] = Twiddle32(attr_data->u_int32_value[i]);
+ }
+ return true;
+ case 64:
+ for(i = 0; i<arraySize; i++){
+ attr_data->u_int64_value[i] = Twiddle64(attr_data->u_int64_value[i]);
+ }
+ return true;
+ default:
+ return false;
+ } // switch
+
+} // Twiddle
+
+FilteredNdbOut err(* new FileOutputStream(stderr), 0, 0);
+FilteredNdbOut info(* new FileOutputStream(stdout), 1, 1);
+FilteredNdbOut debug(* new FileOutputStream(stdout), 2, 0);
+
+// To decide in what byte order data is
+const Uint32 magicByteOrder = 0x12345678;
+const Uint32 swappedMagicByteOrder = 0x78563412;
+
+RestoreMetaData::RestoreMetaData(const char* path, Uint32 nodeId, Uint32 bNo) {
+
+ debug << "RestoreMetaData constructor" << endl;
+ setCtlFile(nodeId, bNo, path);
+}
+
+RestoreMetaData::~RestoreMetaData(){
+ for(Uint32 i= 0; i < allTables.size(); i++)
+ delete allTables[i];
+ allTables.clear();
+}
+
+TableS *
+RestoreMetaData::getTable(Uint32 tableId) const {
+ for(Uint32 i= 0; i < allTables.size(); i++)
+ if(allTables[i]->getTableId() == tableId)
+ return allTables[i];
+ return NULL;
+}
+
+Uint32
+RestoreMetaData::getStopGCP() const {
+ return m_stopGCP;
+}
+
+int
+RestoreMetaData::loadContent()
+{
+ Uint32 noOfTables = readMetaTableList();
+ if(noOfTables == 0) {
+ return 1;
+ }
+ for(Uint32 i = 0; i<noOfTables; i++){
+ if(!readMetaTableDesc()){
+ return 0;
+ }
+ }
+ if(!readGCPEntry())
+ return 0;
+ return 1;
+}
+
+Uint32
+RestoreMetaData::readMetaTableList() {
+
+ Uint32 sectionInfo[2];
+
+ if (buffer_read(&sectionInfo, sizeof(sectionInfo), 1) != 1){
+ err << "readMetaTableList read header error" << endl;
+ return 0;
+ }
+ sectionInfo[0] = ntohl(sectionInfo[0]);
+ sectionInfo[1] = ntohl(sectionInfo[1]);
+
+ const Uint32 tabCount = sectionInfo[1] - 2;
+
+ void *tmp;
+ if (buffer_get_ptr(&tmp, 4, tabCount) != tabCount){
+ err << "readMetaTableList read tabCount error" << endl;
+ return 0;
+ }
+
+ return tabCount;
+}
+
+bool
+RestoreMetaData::readMetaTableDesc() {
+
+ Uint32 sectionInfo[2];
+
+ // Read section header
+ if (buffer_read(&sectionInfo, sizeof(sectionInfo), 1) != 1){
+ err << "readMetaTableDesc read header error" << endl;
+ return false;
+ } // if
+ sectionInfo[0] = ntohl(sectionInfo[0]);
+ sectionInfo[1] = ntohl(sectionInfo[1]);
+
+ assert(sectionInfo[0] == BackupFormat::TABLE_DESCRIPTION);
+
+ // Read dictTabInfo buffer
+ const Uint32 len = (sectionInfo[1] - 2);
+ void *ptr;
+ if (buffer_get_ptr(&ptr, 4, len) != len){
+ err << "readMetaTableDesc read error" << endl;
+ return false;
+ } // if
+
+ return parseTableDescriptor((Uint32*)ptr, len);
+}
+
+bool
+RestoreMetaData::readGCPEntry() {
+
+ Uint32 data[4];
+
+ BackupFormat::CtlFile::GCPEntry * dst =
+ (BackupFormat::CtlFile::GCPEntry *)&data[0];
+
+ if(buffer_read(dst, 4, 4) != 4){
+ err << "readGCPEntry read error" << endl;
+ return false;
+ }
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+
+ if(dst->SectionType != BackupFormat::GCP_ENTRY){
+ err << "readGCPEntry invalid format" << endl;
+ return false;
+ }
+
+ dst->StartGCP = ntohl(dst->StartGCP);
+ dst->StopGCP = ntohl(dst->StopGCP);
+
+ m_startGCP = dst->StartGCP;
+ m_stopGCP = dst->StopGCP;
+ return true;
+}
+
+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));
+}
+
+TableS::~TableS()
+{
+ for (Uint32 i= 0; i < allAttributesDesc.size(); i++)
+ delete allAttributesDesc[i];
+}
+
+// Parse dictTabInfo buffer and pushback to to vector storage
+bool
+RestoreMetaData::parseTableDescriptor(const Uint32 * data, Uint32 len)
+{
+ NdbTableImpl* tableImpl = 0;
+ int ret = NdbDictInterface::parseTableInfo(&tableImpl, data, len, false);
+
+ if (ret != 0) {
+ err << "parseTableInfo " << " failed" << endl;
+ return false;
+ }
+ if(tableImpl == 0)
+ return false;
+
+ debug << "parseTableInfo " << tableImpl->getName() << " done" << endl;
+
+ TableS * table = new TableS(m_fileHeader.NdbVersion, tableImpl);
+ if(table == NULL) {
+ return false;
+ }
+
+ debug << "Parsed table id " << table->getTableId() << endl;
+ debug << "Parsed table #attr " << table->getNoOfAttributes() << endl;
+ debug << "Parsed table schema version not used " << endl;
+
+ debug << "Pushing table " << table->getTableName() << endl;
+ debug << " with " << table->getNoOfAttributes() << " attributes" << endl;
+
+ allTables.push_back(table);
+
+ return true;
+}
+
+// Constructor
+RestoreDataIterator::RestoreDataIterator(const RestoreMetaData & md, void (* _free_data_callback)())
+ : BackupFile(_free_data_callback), m_metaData(md)
+{
+ debug << "RestoreDataIterator constructor" << endl;
+ setDataFile(md, 0);
+}
+
+TupleS & TupleS::operator=(const TupleS& tuple)
+{
+ prepareRecord(*tuple.m_currentTable);
+
+ if (allAttrData)
+ memcpy(allAttrData, tuple.allAttrData, getNoOfAttributes()*sizeof(AttributeData));
+
+ return *this;
+}
+int TupleS::getNoOfAttributes() const {
+ if (m_currentTable == 0)
+ return 0;
+ return m_currentTable->getNoOfAttributes();
+}
+
+TableS * TupleS::getTable() const {
+ return m_currentTable;
+}
+
+const AttributeDesc * TupleS::getDesc(int i) const {
+ return m_currentTable->allAttributesDesc[i];
+}
+
+AttributeData * TupleS::getData(int i) const{
+ return &(allAttrData[i]);
+}
+
+bool
+TupleS::prepareRecord(TableS & tab){
+ if (allAttrData) {
+ if (getNoOfAttributes() == tab.getNoOfAttributes())
+ {
+ m_currentTable = &tab;
+ return true;
+ }
+ delete [] allAttrData;
+ m_currentTable= 0;
+ }
+
+ allAttrData = new AttributeData[tab.getNoOfAttributes()];
+ if (allAttrData == 0)
+ return false;
+
+ m_currentTable = &tab;
+
+ return true;
+}
+
+const TupleS *
+RestoreDataIterator::getNextTuple(int & res)
+{
+ Uint32 dataLength = 0;
+ // Read record length
+ if (buffer_read(&dataLength, sizeof(dataLength), 1) != 1){
+ err << "getNextTuple:Error reading length of data part" << endl;
+ res = -1;
+ return NULL;
+ } // if
+
+ // Convert length from network byte order
+ dataLength = ntohl(dataLength);
+ const Uint32 dataLenBytes = 4 * dataLength;
+
+ if (dataLength == 0) {
+ // Zero length for last tuple
+ // End of this data fragment
+ debug << "End of fragment" << endl;
+ res = 0;
+ return NULL;
+ } // if
+
+ // Read tuple data
+ void *_buf_ptr;
+ if (buffer_get_ptr(&_buf_ptr, 1, dataLenBytes) != dataLenBytes) {
+ err << "getNextTuple:Read error: " << endl;
+ res = -1;
+ return NULL;
+ }
+
+ 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);
+
+ const Uint32 attrId = m_currentTable->m_fixedAttribs[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_variableAttribs.size(); i++){
+ const Uint32 attrId = m_currentTable->m_variableAttribs[i]->attrId;
+
+ AttributeData * attr_data = m_tuple.getData(attrId);
+ const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
+
+ if(attr_desc->m_column->getNullable()){
+ const Uint32 ind = attr_desc->m_nullBitIndex;
+ if(BitmaskImpl::get(m_currentTable->m_nullBitmaskSize,
+ buf_ptr,ind)){
+ attr_data->null = true;
+ attr_data->void_value = NULL;
+ continue;
+ }
+ }
+
+ assert(ptr < buf_ptr + dataLength);
+
+ typedef BackupFormat::DataFile::VariableData VarData;
+ VarData * data = (VarData *)ptr;
+ Uint32 sz = ntohl(data->Sz);
+ Uint32 id = ntohl(data->Id);
+ assert(id == attrId);
+
+ attr_data->null = false;
+ attr_data->void_value = &data->Data[0];
+
+ /**
+ * Compute array size
+ */
+ const Uint32 arraySize = (4 * sz) / (attr_desc->size / 8);
+ assert(arraySize >= attr_desc->arraySize);
+ if(!Twiddle(attr_desc, attr_data, attr_desc->arraySize))
+ {
+ res = -1;
+ return NULL;
+ }
+
+ ptr += (sz + 2);
+ }
+
+ m_count ++;
+ res = 0;
+ return &m_tuple;
+} // RestoreDataIterator::getNextTuple
+
+BackupFile::BackupFile(void (* _free_data_callback)())
+ : free_data_callback(_free_data_callback)
+{
+ m_file = 0;
+ m_path[0] = 0;
+ m_fileName[0] = 0;
+
+ m_buffer_sz = 64*1024;
+ m_buffer = malloc(m_buffer_sz);
+ m_buffer_ptr = m_buffer;
+ m_buffer_data_left = 0;
+}
+
+BackupFile::~BackupFile(){
+ if(m_file != 0)
+ fclose(m_file);
+ if(m_buffer != 0)
+ free(m_buffer);
+}
+
+bool
+BackupFile::openFile(){
+ if(m_file != NULL){
+ fclose(m_file);
+ m_file = 0;
+ }
+
+ m_file = fopen(m_fileName, "r");
+ return m_file != 0;
+}
+
+Uint32 BackupFile::buffer_get_ptr_ahead(void **p_buf_ptr, Uint32 size, Uint32 nmemb)
+{
+ Uint32 sz = size*nmemb;
+ if (sz > m_buffer_data_left) {
+
+ if (free_data_callback)
+ (*free_data_callback)();
+
+ memcpy(m_buffer, m_buffer_ptr, m_buffer_data_left);
+
+ size_t r = fread(((char *)m_buffer) + m_buffer_data_left, 1, m_buffer_sz - m_buffer_data_left, m_file);
+ m_buffer_data_left += r;
+ m_buffer_ptr = m_buffer;
+
+ if (sz > m_buffer_data_left)
+ sz = size * (m_buffer_data_left / size);
+ }
+
+ *p_buf_ptr = m_buffer_ptr;
+
+ return sz/size;
+}
+Uint32 BackupFile::buffer_get_ptr(void **p_buf_ptr, Uint32 size, Uint32 nmemb)
+{
+ Uint32 r = buffer_get_ptr_ahead(p_buf_ptr, size, nmemb);
+
+ m_buffer_ptr = ((char*)m_buffer_ptr)+(r*size);
+ m_buffer_data_left -= (r*size);
+
+ return r;
+}
+
+Uint32 BackupFile::buffer_read_ahead(void *ptr, Uint32 size, Uint32 nmemb)
+{
+ void *buf_ptr;
+ Uint32 r = buffer_get_ptr_ahead(&buf_ptr, size, nmemb);
+ memcpy(ptr, buf_ptr, r*size);
+
+ return r;
+}
+
+Uint32 BackupFile::buffer_read(void *ptr, Uint32 size, Uint32 nmemb)
+{
+ void *buf_ptr;
+ Uint32 r = buffer_get_ptr(&buf_ptr, size, nmemb);
+ memcpy(ptr, buf_ptr, r*size);
+
+ return r;
+}
+
+void
+BackupFile::setCtlFile(Uint32 nodeId, Uint32 backupId, const char * path){
+ m_nodeId = nodeId;
+ m_expectedFileHeader.BackupId = backupId;
+ m_expectedFileHeader.FileType = BackupFormat::CTL_FILE;
+
+ char name[PATH_MAX]; const Uint32 sz = sizeof(name);
+ BaseString::snprintf(name, sz, "BACKUP-%d.%d.ctl", backupId, nodeId);
+ setName(path, name);
+}
+
+void
+BackupFile::setDataFile(const BackupFile & bf, Uint32 no){
+ m_nodeId = bf.m_nodeId;
+ m_expectedFileHeader = bf.m_fileHeader;
+ m_expectedFileHeader.FileType = BackupFormat::DATA_FILE;
+
+ char name[PATH_MAX]; const Uint32 sz = sizeof(name);
+ BaseString::snprintf(name, sz, "BACKUP-%d-%d.%d.Data",
+ m_expectedFileHeader.BackupId, no, m_nodeId);
+ setName(bf.m_path, name);
+}
+
+void
+BackupFile::setLogFile(const BackupFile & bf, Uint32 no){
+ m_nodeId = bf.m_nodeId;
+ m_expectedFileHeader = bf.m_fileHeader;
+ m_expectedFileHeader.FileType = BackupFormat::LOG_FILE;
+
+ char name[PATH_MAX]; const Uint32 sz = sizeof(name);
+ BaseString::snprintf(name, sz, "BACKUP-%d.%d.log",
+ m_expectedFileHeader.BackupId, m_nodeId);
+ setName(bf.m_path, name);
+}
+
+void
+BackupFile::setName(const char * p, const char * n){
+ const Uint32 sz = sizeof(m_path);
+ if(p != 0 && strlen(p) > 0){
+ if(p[strlen(p)-1] == '/'){
+ BaseString::snprintf(m_path, sz, "%s", p);
+ } else {
+ BaseString::snprintf(m_path, sz, "%s%s", p, "/");
+ }
+ } else {
+ m_path[0] = 0;
+ }
+
+ BaseString::snprintf(m_fileName, sizeof(m_fileName), "%s%s", m_path, n);
+ debug << "Filename = " << m_fileName << endl;
+}
+
+bool
+BackupFile::readHeader(){
+ if(!openFile()){
+ return false;
+ }
+
+ if(buffer_read(&m_fileHeader, sizeof(m_fileHeader), 1) != 1){
+ err << "readDataFileHeader: Error reading header" << endl;
+ return false;
+ }
+
+ // Convert from network to host byte order for platform compatibility
+ m_fileHeader.NdbVersion = ntohl(m_fileHeader.NdbVersion);
+ m_fileHeader.SectionType = ntohl(m_fileHeader.SectionType);
+ m_fileHeader.SectionLength = ntohl(m_fileHeader.SectionLength);
+ m_fileHeader.FileType = ntohl(m_fileHeader.FileType);
+ m_fileHeader.BackupId = ntohl(m_fileHeader.BackupId);
+ m_fileHeader.BackupKey_0 = ntohl(m_fileHeader.BackupKey_0);
+ m_fileHeader.BackupKey_1 = ntohl(m_fileHeader.BackupKey_1);
+
+ debug << "FileHeader: " << m_fileHeader.Magic << " " <<
+ m_fileHeader.NdbVersion << " " <<
+ m_fileHeader.SectionType << " " <<
+ m_fileHeader.SectionLength << " " <<
+ m_fileHeader.FileType << " " <<
+ m_fileHeader.BackupId << " " <<
+ m_fileHeader.BackupKey_0 << " " <<
+ m_fileHeader.BackupKey_1 << " " <<
+ m_fileHeader.ByteOrder << endl;
+
+ debug << "ByteOrder is " << m_fileHeader.ByteOrder << endl;
+ debug << "magicByteOrder is " << magicByteOrder << endl;
+
+ if (m_fileHeader.FileType != m_expectedFileHeader.FileType){
+ abort();
+ }
+
+ // Check for BackupFormat::FileHeader::ByteOrder if swapping is needed
+ if (m_fileHeader.ByteOrder == magicByteOrder) {
+ m_hostByteOrder = true;
+ } else if (m_fileHeader.ByteOrder == swappedMagicByteOrder){
+ m_hostByteOrder = false;
+ } else {
+ abort();
+ }
+
+ return true;
+} // BackupFile::readHeader
+
+bool
+BackupFile::validateFooter(){
+ return true;
+}
+
+bool RestoreDataIterator::readFragmentHeader(int & ret)
+{
+ BackupFormat::DataFile::FragmentHeader Header;
+
+ debug << "RestoreDataIterator::getNextFragment" << endl;
+
+ if (buffer_read(&Header, sizeof(Header), 1) != 1){
+ ret = 0;
+ return false;
+ } // if
+
+ Header.SectionType = ntohl(Header.SectionType);
+ Header.SectionLength = ntohl(Header.SectionLength);
+ Header.TableId = ntohl(Header.TableId);
+ Header.FragmentNo = ntohl(Header.FragmentNo);
+ Header.ChecksumType = ntohl(Header.ChecksumType);
+
+ debug << "FragmentHeader: " << Header.SectionType
+ << " " << Header.SectionLength
+ << " " << Header.TableId
+ << " " << Header.FragmentNo
+ << " " << Header.ChecksumType << endl;
+
+ m_currentTable = m_metaData.getTable(Header.TableId);
+ if(m_currentTable == 0){
+ ret = -1;
+ return false;
+ }
+
+ if(!m_tuple.prepareRecord(*m_currentTable))
+ {
+ ret =-1;
+ return false;
+ }
+
+ info << "_____________________________________________________" << endl
+ << "Restoring data in table: " << m_currentTable->getTableName()
+ << "(" << Header.TableId << ") fragment "
+ << Header.FragmentNo << endl;
+
+ m_count = 0;
+ ret = 0;
+
+ return true;
+} // RestoreDataIterator::getNextFragment
+
+
+bool
+RestoreDataIterator::validateFragmentFooter() {
+ BackupFormat::DataFile::FragmentFooter footer;
+
+ if (buffer_read(&footer, sizeof(footer), 1) != 1){
+ err << "getFragmentFooter:Error reading fragment footer" << endl;
+ return false;
+ }
+
+ // TODO: Handle footer, nothing yet
+ footer.SectionType = ntohl(footer.SectionType);
+ footer.SectionLength = ntohl(footer.SectionLength);
+ footer.TableId = ntohl(footer.TableId);
+ footer.FragmentNo = ntohl(footer.FragmentNo);
+ footer.NoOfRecords = ntohl(footer.NoOfRecords);
+ footer.Checksum = ntohl(footer.Checksum);
+
+ assert(m_count == footer.NoOfRecords);
+
+ return true;
+} // RestoreDataIterator::getFragmentFooter
+
+AttributeDesc::AttributeDesc(NdbDictionary::Column *c)
+ : m_column(c)
+{
+ size = 8*NdbColumnImpl::getImpl(* c).m_attrSize;
+ arraySize = NdbColumnImpl::getImpl(* c).m_arraySize;
+}
+
+void TableS::createAttr(NdbDictionary::Column *column)
+{
+ AttributeDesc * d = new AttributeDesc(column);
+ if(d == NULL) {
+ ndbout_c("Restore: Failed to allocate memory");
+ abort();
+ }
+ d->attrId = allAttributesDesc.size();
+ allAttributesDesc.push_back(d);
+
+ 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);
+ return;
+ }
+
+ /* Nullable attr*/
+ d->m_nullBitIndex = m_noOfNullable;
+ m_noOfNullable++;
+ m_nullBitmaskSize = (m_noOfNullable + 31) / 32;
+ m_variableAttribs.push_back(d);
+} // TableS::createAttr
+
+Uint16 Twiddle16(Uint16 in)
+{
+ Uint16 retVal = 0;
+
+ retVal = ((in & 0xFF00) >> 8) |
+ ((in & 0x00FF) << 8);
+
+ return(retVal);
+} // Twiddle16
+
+Uint32 Twiddle32(Uint32 in)
+{
+ Uint32 retVal = 0;
+
+ retVal = ((in & 0x000000FF) << 24) |
+ ((in & 0x0000FF00) << 8) |
+ ((in & 0x00FF0000) >> 8) |
+ ((in & 0xFF000000) >> 24);
+
+ return(retVal);
+} // Twiddle32
+
+Uint64 Twiddle64(Uint64 in)
+{
+ Uint64 retVal = 0;
+
+ retVal =
+ ((in & (Uint64)0x00000000000000FFLL) << 56) |
+ ((in & (Uint64)0x000000000000FF00LL) << 40) |
+ ((in & (Uint64)0x0000000000FF0000LL) << 24) |
+ ((in & (Uint64)0x00000000FF000000LL) << 8) |
+ ((in & (Uint64)0x000000FF00000000LL) >> 8) |
+ ((in & (Uint64)0x0000FF0000000000LL) >> 24) |
+ ((in & (Uint64)0x00FF000000000000LL) >> 40) |
+ ((in & (Uint64)0xFF00000000000000LL) >> 56);
+
+ return(retVal);
+} // Twiddle64
+
+
+RestoreLogIterator::RestoreLogIterator(const RestoreMetaData & md)
+ : m_metaData(md)
+{
+ debug << "RestoreLog constructor" << endl;
+ setLogFile(md, 0);
+
+ m_count = 0;
+}
+
+const LogEntry *
+RestoreLogIterator::getNextLogEntry(int & res) {
+ // Read record length
+ typedef BackupFormat::LogFile::LogEntry LogE;
+
+ Uint32 gcp= 0;
+ LogE * logE= 0;
+ Uint32 len= ~0;
+ const Uint32 stopGCP = m_metaData.getStopGCP();
+ do {
+ if (buffer_read_ahead(&len, sizeof(Uint32), 1) != 1){
+ res= -1;
+ return 0;
+ }
+ len= ntohl(len);
+
+ Uint32 data_len = sizeof(Uint32) + len*4;
+ if (buffer_get_ptr((void **)(&logE), 1, data_len) != data_len) {
+ res= -2;
+ return 0;
+ }
+
+ if(len == 0){
+ res= 0;
+ return 0;
+ }
+
+ logE->TableId= ntohl(logE->TableId);
+ logE->TriggerEvent= ntohl(logE->TriggerEvent);
+
+ const bool hasGcp= (logE->TriggerEvent & 0x10000) != 0;
+ logE->TriggerEvent &= 0xFFFF;
+
+ if(hasGcp){
+ len--;
+ gcp = ntohl(logE->Data[len-2]);
+ }
+ } while(gcp > stopGCP + 1);
+
+ m_logEntry.m_table = m_metaData.getTable(logE->TableId);
+ switch(logE->TriggerEvent){
+ case TriggerEvent::TE_INSERT:
+ m_logEntry.m_type = LogEntry::LE_INSERT;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ m_logEntry.m_type = LogEntry::LE_UPDATE;
+ break;
+ case TriggerEvent::TE_DELETE:
+ m_logEntry.m_type = LogEntry::LE_DELETE;
+ break;
+ default:
+ res = -1;
+ return NULL;
+ }
+
+ const TableS * tab = m_logEntry.m_table;
+ m_logEntry.clear();
+
+ AttributeHeader * ah = (AttributeHeader *)&logE->Data[0];
+ AttributeHeader *end = (AttributeHeader *)&logE->Data[len - 2];
+ AttributeS * attr;
+ while(ah < end){
+ attr= m_logEntry.add_attr();
+ if(attr == NULL) {
+ ndbout_c("Restore: Failed to allocate memory");
+ res = -1;
+ return 0;
+ }
+
+ attr->Desc = (* tab)[ah->getAttributeId()];
+ assert(attr->Desc != 0);
+
+ const Uint32 sz = ah->getDataSize();
+ if(sz == 0){
+ attr->Data.null = true;
+ attr->Data.void_value = NULL;
+ } else {
+ attr->Data.null = false;
+ attr->Data.void_value = ah->getDataPtr();
+ }
+
+ Twiddle(attr->Desc, &(attr->Data));
+
+ ah = ah->getNext();
+ }
+
+ m_count ++;
+ res = 0;
+ return &m_logEntry;
+}
+
+NdbOut &
+operator<<(NdbOut& ndbout, const AttributeS& attr){
+ const AttributeData & data = attr.Data;
+ const AttributeDesc & desc = *(attr.Desc);
+
+ if (data.null)
+ {
+ ndbout << "<NULL>";
+ return ndbout;
+ }
+
+ NdbRecAttr tmprec;
+ tmprec.setup(desc.m_column, (char *)data.void_value);
+ ndbout << tmprec;
+
+ return ndbout;
+}
+
+// Print tuple data
+NdbOut&
+operator<<(NdbOut& ndbout, const TupleS& tuple)
+{
+ ndbout << tuple.getTable()->getTableName() << "; ";
+ for (int i = 0; i < tuple.getNoOfAttributes(); i++)
+ {
+ AttributeData * attr_data = tuple.getData(i);
+ const AttributeDesc * attr_desc = tuple.getDesc(i);
+ const AttributeS attr = {attr_desc, *attr_data};
+ debug << i << " " << attr_desc->m_column->getName();
+ ndbout << attr;
+
+ if (i != (tuple.getNoOfAttributes() - 1))
+ ndbout << delimiter << " ";
+ } // for
+ return ndbout;
+}
+
+// Print tuple data
+NdbOut&
+operator<<(NdbOut& ndbout, const LogEntry& logE)
+{
+ switch(logE.m_type)
+ {
+ case LogEntry::LE_INSERT:
+ ndbout << "INSERT " << logE.m_table->getTableName() << " ";
+ break;
+ case LogEntry::LE_DELETE:
+ ndbout << "DELETE " << logE.m_table->getTableName() << " ";
+ break;
+ case LogEntry::LE_UPDATE:
+ ndbout << "UPDATE " << logE.m_table->getTableName() << " ";
+ break;
+ default:
+ ndbout << "Unknown log entry type (not insert, delete or update)" ;
+ }
+
+ for (Uint32 i= 0; i < logE.size();i++)
+ {
+ const AttributeS * attr = logE[i];
+ ndbout << attr->Desc->m_column->getName() << "=";
+ ndbout << (* attr);
+ if (i < (logE.size() - 1))
+ ndbout << ", ";
+ }
+ return ndbout;
+}
+
+
+NdbOut &
+operator<<(NdbOut& ndbout, const TableS & table){
+ ndbout << endl << "Table: " << table.getTableName() << endl;
+ for (int j = 0; j < table.getNoOfAttributes(); j++)
+ {
+ const AttributeDesc * desc = table[j];
+ ndbout << desc->m_column->getName() << ": "
+ << (Uint32) desc->m_column->getType();
+ ndbout << " key: " << (Uint32) desc->m_column->getPrimaryKey();
+ ndbout << " array: " << desc->arraySize;
+ ndbout << " size: " << desc->size << endl;
+ } // for
+ return ndbout;
+}
+
+template class Vector<TableS*>;
+template class Vector<AttributeS*>;
+template class Vector<AttributeDesc*>;
+
diff --git a/storage/ndb/tools/restore/Restore.hpp b/storage/ndb/tools/restore/Restore.hpp
new file mode 100644
index 00000000000..d7f6e3b7799
--- /dev/null
+++ b/storage/ndb/tools/restore/Restore.hpp
@@ -0,0 +1,379 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef RESTORE_H
+#define RESTORE_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include "../src/kernel/blocks/backup/BackupFormat.hpp"
+#include "../src/ndbapi/NdbDictionaryImpl.hpp"
+#include <NdbApi.hpp>
+
+#include <ndb_version.h>
+#include <version.h>
+
+static const char * delimiter = ";"; // Delimiter in file dump
+
+const int FileNameLenC = 256;
+const int TableNameLenC = 256;
+const int AttrNameLenC = 256;
+const Uint32 timeToWaitForNdbC = 10000;
+const Uint32 opsDefaultC = 1000;
+
+// Forward declarations
+//class AttributeDesc;
+struct AttributeDesc;
+struct AttributeData;
+struct AttributeS;
+
+struct AttributeData {
+ bool null;
+ Uint32 size;
+ union {
+ Int8 * int8_value;
+ Uint8 * u_int8_value;
+
+ Int16 * int16_value;
+ Uint16 * u_int16_value;
+
+ Int32 * int32_value;
+ Uint32 * u_int32_value;
+
+ Int64 * int64_value;
+ Uint64 * u_int64_value;
+
+ char * string_value;
+
+ void* void_value;
+ };
+};
+
+struct AttributeDesc {
+ //private:
+ friend class TupleS;
+ friend class TableS;
+ friend class RestoreDataIterator;
+ friend class RestoreMetaData;
+ friend struct AttributeS;
+ Uint32 size; // bits
+ Uint32 arraySize;
+ Uint32 attrId;
+ NdbDictionary::Column *m_column;
+
+ Uint32 m_nullBitIndex;
+public:
+
+ AttributeDesc(NdbDictionary::Column *column);
+ AttributeDesc();
+
+ Uint32 getSizeInWords() const { return (size * arraySize + 31)/ 32;}
+}; // AttributeDesc
+
+struct AttributeS {
+ const AttributeDesc * Desc;
+ AttributeData Data;
+};
+
+class TupleS {
+private:
+ friend class RestoreDataIterator;
+
+ class TableS *m_currentTable;
+ AttributeData *allAttrData;
+ bool prepareRecord(TableS &);
+
+public:
+ TupleS() {
+ m_currentTable= 0;
+ allAttrData= 0;
+ };
+ ~TupleS()
+ {
+ if (allAttrData)
+ delete [] allAttrData;
+ };
+ TupleS(const TupleS& tuple); // disable copy constructor
+ TupleS & operator=(const TupleS& tuple);
+ int getNoOfAttributes() const;
+ TableS * getTable() const;
+ const AttributeDesc * getDesc(int i) const;
+ AttributeData * getData(int i) const;
+}; // class TupleS
+
+class TableS {
+
+ friend class TupleS;
+ friend class RestoreMetaData;
+ friend class RestoreDataIterator;
+
+ Uint32 schemaVersion;
+ Uint32 backupVersion;
+ Vector<AttributeDesc *> allAttributesDesc;
+ Vector<AttributeDesc *> m_fixedKeys;
+ //Vector<AttributeDesc *> m_variableKey;
+ Vector<AttributeDesc *> m_fixedAttribs;
+ Vector<AttributeDesc *> m_variableAttribs;
+
+ Uint32 m_noOfNullable;
+ Uint32 m_nullBitmaskSize;
+
+ Uint32 m_auto_val_id;
+ Uint64 m_max_auto_val;
+
+ int pos;
+
+ void createAttr(NdbDictionary::Column *column);
+
+public:
+ class NdbDictionary::Table* m_dictTable;
+ TableS (Uint32 version, class NdbTableImpl* dictTable);
+ ~TableS();
+
+ Uint32 getTableId() const {
+ return m_dictTable->getTableId();
+ }
+ /*
+ void setMysqlTableName(char * tableName) {
+ strpcpy(mysqlTableName, tableName);
+ }
+
+ char *
+ void setMysqlDatabaseName(char * databaseName) {
+ strpcpy(mysqlDatabaseName, databaseName);
+ }
+
+ table.setMysqlDatabaseName(database);
+ */
+ void setBackupVersion(Uint32 version) {
+ backupVersion = version;
+ }
+
+ Uint32 getBackupVersion() const {
+ return backupVersion;
+ }
+
+ const char * getTableName() const {
+ return m_dictTable->getName();
+ }
+
+ int getNoOfAttributes() const {
+ return allAttributesDesc.size();
+ };
+
+ bool have_auto_inc() const {
+ return m_auto_val_id != ~(Uint32)0;
+ };
+
+ bool have_auto_inc(Uint32 id) const {
+ return m_auto_val_id == id;
+ };
+
+ Uint64 get_max_auto_val() const {
+ return m_max_auto_val;
+ };
+
+ void update_max_auto_val(const char *data, int size) {
+ union {
+ Uint8 u8;
+ Uint16 u16;
+ Uint32 u32;
+ } val;
+ Uint64 v;
+ switch(size){
+ case 64:
+ memcpy(&v,data,8);
+ break;
+ case 32:
+ memcpy(&val.u32,data,4);
+ v= val.u32;
+ break;
+ case 16:
+ memcpy(&val.u16,data,2);
+ v= val.u16;
+ break;
+ case 8:
+ memcpy(&val.u8,data,1);
+ v= val.u8;
+ break;
+ default:
+ return;
+ };
+ if(v > m_max_auto_val)
+ m_max_auto_val= v;
+ };
+ /**
+ * Get attribute descriptor
+ */
+ const AttributeDesc * operator[](int attributeId) const {
+ return allAttributesDesc[attributeId];
+ }
+
+ TableS& operator=(TableS& org) ;
+}; // TableS;
+
+class BackupFile {
+protected:
+ FILE * m_file;
+ char m_path[PATH_MAX];
+ char m_fileName[PATH_MAX];
+ bool m_hostByteOrder;
+ BackupFormat::FileHeader m_fileHeader;
+ BackupFormat::FileHeader m_expectedFileHeader;
+
+ Uint32 m_nodeId;
+
+ void * m_buffer;
+ void * m_buffer_ptr;
+ Uint32 m_buffer_sz;
+ Uint32 m_buffer_data_left;
+ void (* free_data_callback)();
+
+ bool openFile();
+ void setCtlFile(Uint32 nodeId, Uint32 backupId, const char * path);
+ void setDataFile(const BackupFile & bf, Uint32 no);
+ void setLogFile(const BackupFile & bf, Uint32 no);
+
+ Uint32 buffer_get_ptr(void **p_buf_ptr, Uint32 size, Uint32 nmemb);
+ Uint32 buffer_read(void *ptr, Uint32 size, Uint32 nmemb);
+ Uint32 buffer_get_ptr_ahead(void **p_buf_ptr, Uint32 size, Uint32 nmemb);
+ Uint32 buffer_read_ahead(void *ptr, Uint32 size, Uint32 nmemb);
+
+ void setName(const char * path, const char * name);
+
+ BackupFile(void (* free_data_callback)() = 0);
+ ~BackupFile();
+public:
+ bool readHeader();
+ bool validateFooter();
+
+ const char * getPath() const { return m_path;}
+ const char * getFilename() const { return m_fileName;}
+ Uint32 getNodeId() const { return m_nodeId;}
+ const BackupFormat::FileHeader & getFileHeader() const { return m_fileHeader;}
+ bool Twiddle(const AttributeDesc * attr_desc, AttributeData * attr_data, Uint32 arraySize = 0);
+};
+
+class RestoreMetaData : public BackupFile {
+
+ Vector<TableS *> allTables;
+ bool readMetaFileHeader();
+ bool readMetaTableDesc();
+
+ bool readGCPEntry();
+ Uint32 readMetaTableList();
+
+ Uint32 m_startGCP;
+ Uint32 m_stopGCP;
+
+ bool parseTableDescriptor(const Uint32 * data, Uint32 len);
+
+public:
+ RestoreMetaData(const char * path, Uint32 nodeId, Uint32 bNo);
+ virtual ~RestoreMetaData();
+
+ int loadContent();
+
+ Uint32 getNoOfTables() const { return allTables.size();}
+
+ const TableS * operator[](int i) const { return allTables[i];}
+ TableS * getTable(Uint32 tableId) const;
+
+ Uint32 getStopGCP() const;
+}; // RestoreMetaData
+
+
+class RestoreDataIterator : public BackupFile {
+ const RestoreMetaData & m_metaData;
+ Uint32 m_count;
+ TableS* m_currentTable;
+ TupleS m_tuple;
+
+public:
+
+ // Constructor
+ RestoreDataIterator(const RestoreMetaData &, void (* free_data_callback)());
+ ~RestoreDataIterator() {};
+
+ // Read data file fragment header
+ bool readFragmentHeader(int & res);
+ bool validateFragmentFooter();
+
+ const TupleS *getNextTuple(int & res);
+};
+
+class LogEntry {
+public:
+ enum EntryType {
+ LE_INSERT,
+ LE_DELETE,
+ LE_UPDATE
+ };
+ EntryType m_type;
+ TableS * m_table;
+ Vector<AttributeS*> m_values;
+ Vector<AttributeS*> m_values_e;
+ AttributeS *add_attr() {
+ AttributeS * attr;
+ if (m_values_e.size() > 0) {
+ attr = m_values_e[m_values_e.size()-1];
+ m_values_e.erase(m_values_e.size()-1);
+ }
+ else
+ {
+ attr = new AttributeS;
+ }
+ m_values.push_back(attr);
+ return attr;
+ }
+ void clear() {
+ for(Uint32 i= 0; i < m_values.size(); i++)
+ m_values_e.push_back(m_values[i]);
+ m_values.clear();
+ }
+ ~LogEntry()
+ {
+ Uint32 i;
+ for(i= 0; i< m_values.size(); i++)
+ delete m_values[i];
+ for(i= 0; i< m_values_e.size(); i++)
+ delete m_values_e[i];
+ }
+ Uint32 size() const { return m_values.size(); }
+ const AttributeS * operator[](int i) const { return m_values[i];}
+};
+
+class RestoreLogIterator : public BackupFile {
+private:
+ const RestoreMetaData & m_metaData;
+
+ Uint32 m_count;
+ LogEntry m_logEntry;
+public:
+ RestoreLogIterator(const RestoreMetaData &);
+ virtual ~RestoreLogIterator() {};
+
+ const LogEntry * getNextLogEntry(int & res);
+};
+
+NdbOut& operator<<(NdbOut& ndbout, const TableS&);
+NdbOut& operator<<(NdbOut& ndbout, const TupleS&);
+NdbOut& operator<<(NdbOut& ndbout, const LogEntry&);
+NdbOut& operator<<(NdbOut& ndbout, const RestoreMetaData&);
+
+#endif
+
+
diff --git a/storage/ndb/tools/restore/consumer.cpp b/storage/ndb/tools/restore/consumer.cpp
new file mode 100644
index 00000000000..b130c4998d5
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer.cpp
@@ -0,0 +1,115 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "consumer.hpp"
+
+#ifdef USE_MYSQL
+int
+BackupConsumer::create_table_string(const TableS & table,
+ char * tableName,
+ char *buf){
+ int pos = 0;
+ int pos2 = 0;
+ char buf2[2048];
+
+ pos += sprintf(buf+pos, "%s%s", "CREATE TABLE ", tableName);
+ pos += sprintf(buf+pos, "%s", "(");
+ pos2 += sprintf(buf2+pos2, "%s", " primary key(");
+
+ for (int j = 0; j < table.getNoOfAttributes(); j++)
+ {
+ const AttributeDesc * desc = table[j];
+ // ndbout << desc->name << ": ";
+ pos += sprintf(buf+pos, "%s%s", desc->m_column->getName()," ");
+ switch(desc->m_column->getType()){
+ case NdbDictionary::Column::Int:
+ pos += sprintf(buf+pos, "%s", "int");
+ break;
+ case NdbDictionary::Column::Unsigned:
+ pos += sprintf(buf+pos, "%s", "int unsigned");
+ break;
+ case NdbDictionary::Column::Float:
+ pos += sprintf(buf+pos, "%s", "float");
+ break;
+ case NdbDictionary::Column::Olddecimal:
+ case NdbDictionary::Column::Decimal:
+ pos += sprintf(buf+pos, "%s", "decimal");
+ break;
+ case NdbDictionary::Column::Olddecimalunsigned:
+ case NdbDictionary::Column::Decimalunsigned:
+ pos += sprintf(buf+pos, "%s", "decimal unsigned");
+ break;
+ case NdbDictionary::Column::Char:
+ pos += sprintf(buf+pos, "%s", "char");
+ break;
+ case NdbDictionary::Column::Varchar:
+ pos += sprintf(buf+pos, "%s", "varchar");
+ break;
+ case NdbDictionary::Column::Binary:
+ pos += sprintf(buf+pos, "%s", "binary");
+ break;
+ case NdbDictionary::Column::Varbinary:
+ pos += sprintf(buf+pos, "%s", "varchar binary");
+ break;
+ case NdbDictionary::Column::Bigint:
+ pos += sprintf(buf+pos, "%s", "bigint");
+ break;
+ case NdbDictionary::Column::Bigunsigned:
+ pos += sprintf(buf+pos, "%s", "bigint unsigned");
+ break;
+ case NdbDictionary::Column::Double:
+ pos += sprintf(buf+pos, "%s", "double");
+ break;
+ case NdbDictionary::Column::Datetime:
+ pos += sprintf(buf+pos, "%s", "datetime");
+ break;
+ case NdbDictionary::Column::Date:
+ pos += sprintf(buf+pos, "%s", "date");
+ break;
+ case NdbDictionary::Column::Time:
+ pos += sprintf(buf+pos, "%s", "time");
+ break;
+ case NdbDictionary::Column::Undefined:
+ // pos += sprintf(buf+pos, "%s", "varchar binary");
+ return -1;
+ break;
+ default:
+ //pos += sprintf(buf+pos, "%s", "varchar binary");
+ return -1;
+ }
+ if (desc->arraySize > 1) {
+ int attrSize = desc->arraySize;
+ pos += sprintf(buf+pos, "%s%u%s",
+ "(",
+ attrSize,
+ ")");
+ }
+ if (desc->m_column->getPrimaryKey()) {
+ pos += sprintf(buf+pos, "%s", " not null");
+ pos2 += sprintf(buf2+pos2, "%s%s", desc->m_column->getName(), ",");
+ }
+ pos += sprintf(buf+pos, "%s", ",");
+ } // for
+ pos2--; // remove trailing comma
+ pos2 += sprintf(buf2+pos2, "%s", ")");
+ // pos--; // remove trailing comma
+
+ pos += sprintf(buf+pos, "%s", buf2);
+ pos += sprintf(buf+pos, "%s", ") type=ndbcluster");
+ return 0;
+}
+
+#endif // USE_MYSQL
diff --git a/storage/ndb/tools/restore/consumer.hpp b/storage/ndb/tools/restore/consumer.hpp
new file mode 100644
index 00000000000..692c814159f
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer.hpp
@@ -0,0 +1,36 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef CONSUMER_HPP
+#define CONSUMER_HPP
+
+#include "Restore.hpp"
+
+class BackupConsumer {
+public:
+ virtual ~BackupConsumer() { }
+ virtual bool init() { return true;}
+ virtual bool table(const TableS &){return true;}
+ virtual bool endOfTables() { return true; }
+ virtual void tuple(const TupleS &){}
+ virtual void tuple_free(){}
+ virtual void endOfTuples(){}
+ virtual void logEntry(const LogEntry &){}
+ virtual void endOfLogEntrys(){}
+ virtual bool finalize_table(const TableS &){return true;}
+};
+
+#endif
diff --git a/storage/ndb/tools/restore/consumer_printer.cpp b/storage/ndb/tools/restore/consumer_printer.cpp
new file mode 100644
index 00000000000..0aa5b521d29
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer_printer.cpp
@@ -0,0 +1,55 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "consumer_printer.hpp"
+
+bool
+BackupPrinter::table(const TableS & tab)
+{
+ if (m_print || m_print_meta)
+ {
+ m_ndbout << tab;
+ ndbout_c("Successfully printed table: %s", tab.m_dictTable->getName());
+ }
+ return true;
+}
+
+void
+BackupPrinter::tuple(const TupleS & tup)
+{
+ m_dataCount++;
+ if (m_print || m_print_data)
+ m_ndbout << tup << endl;
+}
+
+void
+BackupPrinter::logEntry(const LogEntry & logE)
+{
+ if (m_print || m_print_log)
+ m_ndbout << logE << endl;
+ m_logCount++;
+}
+
+void
+BackupPrinter::endOfLogEntrys()
+{
+ if (m_print || m_print_log)
+ {
+ ndbout << "Printed " << m_dataCount << " tuples and "
+ << m_logCount << " log entries"
+ << " to stdout." << endl;
+ }
+}
diff --git a/storage/ndb/tools/restore/consumer_printer.hpp b/storage/ndb/tools/restore/consumer_printer.hpp
new file mode 100644
index 00000000000..7cbc924e364
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer_printer.hpp
@@ -0,0 +1,50 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef CONSUMER_PRINTER_HPP
+#define CONSUMER_PRINTER_HPP
+
+#include "consumer.hpp"
+
+class BackupPrinter : public BackupConsumer
+{
+ NdbOut & m_ndbout;
+public:
+ BackupPrinter(NdbOut & out = ndbout) : m_ndbout(out)
+ {
+ m_print = false;
+ m_print_log = false;
+ m_print_data = false;
+ m_print_meta = false;
+ }
+
+ virtual bool table(const TableS &);
+#ifdef USE_MYSQL
+ virtual bool table(const TableS &, MYSQL* mysqlp);
+#endif
+ virtual void tuple(const TupleS &);
+ virtual void logEntry(const LogEntry &);
+ virtual void endOfTuples() {};
+ virtual void endOfLogEntrys();
+ bool m_print;
+ bool m_print_log;
+ bool m_print_data;
+ bool m_print_meta;
+ Uint32 m_logCount;
+ Uint32 m_dataCount;
+};
+
+#endif
diff --git a/storage/ndb/tools/restore/consumer_restore.cpp b/storage/ndb/tools/restore/consumer_restore.cpp
new file mode 100644
index 00000000000..d72b82569e2
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer_restore.cpp
@@ -0,0 +1,674 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "consumer_restore.hpp"
+#include <NdbSleep.h>
+
+extern FilteredNdbOut err;
+extern FilteredNdbOut info;
+extern FilteredNdbOut debug;
+
+static void callback(int, NdbTransaction*, void*);
+
+extern const char * g_connect_string;
+bool
+BackupRestore::init()
+{
+ release();
+
+ if (!m_restore && !m_restore_meta)
+ return true;
+
+ m_cluster_connection = new Ndb_cluster_connection(g_connect_string);
+ if(m_cluster_connection->connect(12, 5, 1) != 0)
+ {
+ return -1;
+ }
+
+ m_ndb = new Ndb(m_cluster_connection);
+
+ if (m_ndb == NULL)
+ return false;
+
+ m_ndb->init(1024);
+ if (m_ndb->waitUntilReady(30) != 0)
+ {
+ err << "Failed to connect to ndb!!" << endl;
+ return false;
+ }
+ info << "Connected to ndb!!" << endl;
+
+ m_callback = new restore_callback_t[m_parallelism];
+
+ if (m_callback == 0)
+ {
+ err << "Failed to allocate callback structs" << endl;
+ return false;
+ }
+
+ m_free_callback= m_callback;
+ for (Uint32 i= 0; i < m_parallelism; i++) {
+ m_callback[i].restore= this;
+ m_callback[i].connection= 0;
+ if (i > 0)
+ m_callback[i-1].next= &(m_callback[i]);
+ }
+ m_callback[m_parallelism-1].next = 0;
+
+ return true;
+}
+
+void BackupRestore::release()
+{
+ if (m_ndb)
+ {
+ delete m_ndb;
+ m_ndb= 0;
+ }
+
+ if (m_callback)
+ {
+ delete [] m_callback;
+ m_callback= 0;
+ }
+
+ if (m_cluster_connection)
+ {
+ delete m_cluster_connection;
+ m_cluster_connection= 0;
+ }
+}
+
+BackupRestore::~BackupRestore()
+{
+ release();
+}
+
+static
+int
+match_blob(const char * name){
+ int cnt, id1, id2;
+ char buf[256];
+ if((cnt = sscanf(name, "%[^/]/%[^/]/NDB$BLOB_%d_%d", buf, buf, &id1, &id2)) == 4){
+ return id1;
+ }
+
+ return -1;
+}
+
+const NdbDictionary::Table*
+BackupRestore::get_table(const NdbDictionary::Table* tab){
+ if(m_cache.m_old_table == tab)
+ return m_cache.m_new_table;
+ m_cache.m_old_table = tab;
+
+ int cnt, id1, id2;
+ char db[256], schema[256];
+ if((cnt = sscanf(tab->getName(), "%[^/]/%[^/]/NDB$BLOB_%d_%d",
+ db, schema, &id1, &id2)) == 4){
+ m_ndb->setDatabaseName(db);
+ m_ndb->setSchemaName(schema);
+
+ BaseString::snprintf(db, sizeof(db), "NDB$BLOB_%d_%d",
+ m_new_tables[id1]->getTableId(), id2);
+
+ m_cache.m_new_table = m_ndb->getDictionary()->getTable(db);
+
+ } else {
+ m_cache.m_new_table = m_new_tables[tab->getTableId()];
+ }
+ assert(m_cache.m_new_table);
+ return m_cache.m_new_table;
+}
+
+bool
+BackupRestore::finalize_table(const TableS & table){
+ bool ret= true;
+ if (!m_restore && !m_restore_meta)
+ return ret;
+ if (table.have_auto_inc())
+ {
+ Uint64 max_val= table.get_max_auto_val();
+ Uint64 auto_val= m_ndb->readAutoIncrementValue(get_table(table.m_dictTable));
+ if (max_val+1 > auto_val || auto_val == ~(Uint64)0)
+ ret= m_ndb->setAutoIncrementValue(get_table(table.m_dictTable), max_val+1, false);
+ }
+ return ret;
+}
+
+bool
+BackupRestore::table(const TableS & table){
+ if (!m_restore && !m_restore_meta)
+ return true;
+
+ const char * name = table.getTableName();
+
+ /**
+ * Ignore blob tables
+ */
+ if(match_blob(name) >= 0)
+ return true;
+
+ const NdbTableImpl & tmptab = NdbTableImpl::getImpl(* table.m_dictTable);
+ if(tmptab.m_indexType != NdbDictionary::Index::Undefined){
+ m_indexes.push_back(table.m_dictTable);
+ return true;
+ }
+
+ BaseString tmp(name);
+ Vector<BaseString> split;
+ if(tmp.split(split, "/") != 3){
+ err << "Invalid table name format " << name << endl;
+ return false;
+ }
+
+ m_ndb->setDatabaseName(split[0].c_str());
+ m_ndb->setSchemaName(split[1].c_str());
+
+ NdbDictionary::Dictionary* dict = m_ndb->getDictionary();
+ if(m_restore_meta){
+ NdbDictionary::Table copy(*table.m_dictTable);
+
+ copy.setName(split[2].c_str());
+
+ if (dict->createTable(copy) == -1)
+ {
+ err << "Create table " << table.getTableName() << " failed: "
+ << dict->getNdbError() << endl;
+ return false;
+ }
+ info << "Successfully restored table " << table.getTableName()<< endl ;
+ }
+
+ const NdbDictionary::Table* tab = dict->getTable(split[2].c_str());
+ if(tab == 0){
+ err << "Unable to find table: " << split[2].c_str() << endl;
+ return false;
+ }
+ if(m_restore_meta){
+ m_ndb->setAutoIncrementValue(tab, ~(Uint64)0, false);
+ }
+ const NdbDictionary::Table* null = 0;
+ m_new_tables.fill(table.m_dictTable->getTableId(), null);
+ m_new_tables[table.m_dictTable->getTableId()] = tab;
+ return true;
+}
+
+bool
+BackupRestore::endOfTables(){
+ if(!m_restore_meta)
+ return true;
+
+ NdbDictionary::Dictionary* dict = m_ndb->getDictionary();
+ for(size_t i = 0; i<m_indexes.size(); i++){
+ NdbTableImpl & indtab = NdbTableImpl::getImpl(* m_indexes[i]);
+
+ BaseString tmp(indtab.m_primaryTable.c_str());
+ Vector<BaseString> split;
+ if(tmp.split(split, "/") != 3){
+ err << "Invalid table name format " << indtab.m_primaryTable.c_str()
+ << endl;
+ return false;
+ }
+
+ m_ndb->setDatabaseName(split[0].c_str());
+ m_ndb->setSchemaName(split[1].c_str());
+
+ const NdbDictionary::Table * prim = dict->getTable(split[2].c_str());
+ if(prim == 0){
+ err << "Unable to find base table \"" << split[2].c_str()
+ << "\" for index "
+ << indtab.getName() << endl;
+ return false;
+ }
+ NdbTableImpl& base = NdbTableImpl::getImpl(*prim);
+ NdbIndexImpl* idx;
+ int id;
+ char idxName[255], buf[255];
+ if(sscanf(indtab.getName(), "%[^/]/%[^/]/%d/%s",
+ buf, buf, &id, idxName) != 4){
+ err << "Invalid index name format " << indtab.getName() << endl;
+ return false;
+ }
+ if(NdbDictInterface::create_index_obj_from_table(&idx, &indtab, &base))
+ {
+ err << "Failed to create index " << idxName
+ << " on " << split[2].c_str() << endl;
+ return false;
+ }
+ idx->setName(idxName);
+ if(dict->createIndex(* idx) != 0)
+ {
+ delete idx;
+ err << "Failed to create index " << idxName
+ << " on " << split[2].c_str() << endl
+ << dict->getNdbError() << endl;
+
+ return false;
+ }
+ delete idx;
+ info << "Successfully created index " << idxName
+ << " on " << split[2].c_str() << endl;
+ }
+ return true;
+}
+
+void BackupRestore::tuple(const TupleS & tup)
+{
+ if (!m_restore)
+ return;
+
+ while (m_free_callback == 0)
+ {
+ assert(m_transactions == m_parallelism);
+ // send-poll all transactions
+ // close transaction is done in callback
+ m_ndb->sendPollNdb(3000, 1);
+ }
+
+ restore_callback_t * cb = m_free_callback;
+
+ if (cb == 0)
+ assert(false);
+
+ m_free_callback = cb->next;
+ cb->retries = 0;
+ cb->tup = tup; // must do copy!
+ tuple_a(cb);
+
+}
+
+void BackupRestore::tuple_a(restore_callback_t *cb)
+{
+ while (cb->retries < 10)
+ {
+ /**
+ * start transactions
+ */
+ cb->connection = m_ndb->startTransaction();
+ if (cb->connection == NULL)
+ {
+ /*
+ if (errorHandler(cb))
+ {
+ continue;
+ }
+ */
+ exitHandler();
+ } // if
+
+ const TupleS &tup = cb->tup;
+ const NdbDictionary::Table * table = get_table(tup.getTable()->m_dictTable);
+
+ NdbOperation * op = cb->connection->getNdbOperation(table);
+
+ if (op == NULL)
+ {
+ if (errorHandler(cb))
+ continue;
+ exitHandler();
+ } // if
+
+ if (op->writeTuple() == -1)
+ {
+ if (errorHandler(cb))
+ continue;
+ exitHandler();
+ } // if
+
+ int ret = 0;
+ for (int j = 0; j < 2; j++)
+ {
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeDesc * attr_desc = tup.getDesc(i);
+ const AttributeData * attr_data = tup.getData(i);
+ int size = attr_desc->size;
+ int arraySize = attr_desc->arraySize;
+ char * dataPtr = attr_data->string_value;
+ Uint32 length = (size * arraySize) / 8;
+
+ if (j == 0 && tup.getTable()->have_auto_inc(i))
+ tup.getTable()->update_max_auto_val(dataPtr,size);
+
+ if (attr_desc->m_column->getPrimaryKey())
+ {
+ if (j == 1) continue;
+ ret = op->equal(i, dataPtr, length);
+ }
+ else
+ {
+ if (j == 0) continue;
+ if (attr_data->null)
+ ret = op->setValue(i, NULL, 0);
+ else
+ ret = op->setValue(i, dataPtr, length);
+ }
+ if (ret < 0) {
+ ndbout_c("Column: %d type %d %d %d %d",i,
+ attr_desc->m_column->getType(),
+ size, arraySize, attr_data->size);
+ break;
+ }
+ }
+ if (ret < 0)
+ break;
+ }
+ if (ret < 0)
+ {
+ if (errorHandler(cb))
+ continue;
+ exitHandler();
+ }
+
+ // Prepare transaction (the transaction is NOT yet sent to NDB)
+ cb->connection->executeAsynchPrepare(NdbTransaction::Commit,
+ &callback, cb);
+ m_transactions++;
+ return;
+ }
+ err << "Retried transaction " << cb->retries << " times.\nLast error"
+ << m_ndb->getNdbError(cb->error_code) << endl
+ << "...Unable to recover from errors. Exiting..." << endl;
+ exitHandler();
+}
+
+void BackupRestore::cback(int result, restore_callback_t *cb)
+{
+ m_transactions--;
+
+ if (result < 0)
+ {
+ /**
+ * Error. temporary or permanent?
+ */
+ if (errorHandler(cb))
+ tuple_a(cb); // retry
+ else
+ {
+ err << "Restore: Failed to restore data due to a unrecoverable error. Exiting..." << endl;
+ exitHandler();
+ }
+ }
+ else
+ {
+ /**
+ * OK! close transaction
+ */
+ m_ndb->closeTransaction(cb->connection);
+ cb->connection= 0;
+ cb->next= m_free_callback;
+ m_free_callback= cb;
+ m_dataCount++;
+ }
+}
+
+/**
+ * returns true if is recoverable,
+ * Error handling based on hugo
+ * false if it is an error that generates an abort.
+ */
+bool BackupRestore::errorHandler(restore_callback_t *cb)
+{
+ NdbError error= cb->connection->getNdbError();
+ m_ndb->closeTransaction(cb->connection);
+ cb->connection= 0;
+
+ Uint32 sleepTime = 100 + cb->retries * 300;
+
+ cb->retries++;
+ cb->error_code = error.code;
+
+ switch(error.status)
+ {
+ case NdbError::Success:
+ return false;
+ // ERROR!
+ break;
+
+ case NdbError::TemporaryError:
+ NdbSleep_MilliSleep(sleepTime);
+ return true;
+ // RETRY
+ break;
+
+ case NdbError::UnknownResult:
+ err << error << endl;
+ return false;
+ // ERROR!
+ break;
+
+ default:
+ case NdbError::PermanentError:
+ //ERROR
+ err << error << endl;
+ return false;
+ break;
+ }
+ return false;
+}
+
+void BackupRestore::exitHandler()
+{
+ release();
+ exit(-1);
+}
+
+
+void
+BackupRestore::tuple_free()
+{
+ if (!m_restore)
+ return;
+
+ // Poll all transactions
+ while (m_transactions)
+ {
+ m_ndb->sendPollNdb(3000);
+ }
+}
+
+void
+BackupRestore::endOfTuples()
+{
+ tuple_free();
+}
+
+void
+BackupRestore::logEntry(const LogEntry & tup)
+{
+ if (!m_restore)
+ return;
+
+ NdbTransaction * trans = m_ndb->startTransaction();
+ if (trans == NULL)
+ {
+ // Deep shit, TODO: handle the error
+ err << "Cannot start transaction" << endl;
+ exit(-1);
+ } // if
+
+ const NdbDictionary::Table * table = get_table(tup.m_table->m_dictTable);
+ NdbOperation * op = trans->getNdbOperation(table);
+ if (op == NULL)
+ {
+ err << "Cannot get operation: " << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ int check = 0;
+ switch(tup.m_type)
+ {
+ case LogEntry::LE_INSERT:
+ check = op->insertTuple();
+ break;
+ case LogEntry::LE_UPDATE:
+ check = op->updateTuple();
+ break;
+ case LogEntry::LE_DELETE:
+ check = op->deleteTuple();
+ break;
+ default:
+ err << "Log entry has wrong operation type."
+ << " Exiting...";
+ exit(-1);
+ }
+
+ for (Uint32 i= 0; i < tup.size(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ if (tup.m_table->have_auto_inc(attr->Desc->attrId))
+ tup.m_table->update_max_auto_val(dataPtr,size);
+
+ const Uint32 length = (size / 8) * arraySize;
+ if (attr->Desc->m_column->getPrimaryKey())
+ op->equal(attr->Desc->attrId, dataPtr, length);
+ else
+ op->setValue(attr->Desc->attrId, dataPtr, length);
+ }
+
+ const int ret = trans->execute(NdbTransaction::Commit);
+ if (ret != 0)
+ {
+ // Both insert update and delete can fail during log running
+ // and it's ok
+ // TODO: check that the error is either tuple exists or tuple does not exist?
+ switch(tup.m_type)
+ {
+ case LogEntry::LE_INSERT:
+ break;
+ case LogEntry::LE_UPDATE:
+ break;
+ case LogEntry::LE_DELETE:
+ break;
+ }
+ if (false)
+ {
+ err << "execute failed: " << trans->getNdbError() << endl;
+ exit(-1);
+ }
+ }
+
+ m_ndb->closeTransaction(trans);
+ m_logCount++;
+}
+
+void
+BackupRestore::endOfLogEntrys()
+{
+ if (!m_restore)
+ return;
+
+ info << "Restored " << m_dataCount << " tuples and "
+ << m_logCount << " log entries" << endl;
+}
+
+/*
+ * callback : This is called when the transaction is polled
+ *
+ * (This function must have three arguments:
+ * - The result of the transaction,
+ * - The NdbTransaction object, and
+ * - A pointer to an arbitrary object.)
+ */
+
+static void
+callback(int result, NdbTransaction* trans, void* aObject)
+{
+ restore_callback_t *cb = (restore_callback_t *)aObject;
+ (cb->restore)->cback(result, cb);
+}
+
+#if 0 // old tuple impl
+void
+BackupRestore::tuple(const TupleS & tup)
+{
+ if (!m_restore)
+ return;
+ while (1)
+ {
+ NdbTransaction * trans = m_ndb->startTransaction();
+ if (trans == NULL)
+ {
+ // Deep shit, TODO: handle the error
+ ndbout << "Cannot start transaction" << endl;
+ exit(-1);
+ } // if
+
+ const TableS * table = tup.getTable();
+ NdbOperation * op = trans->getNdbOperation(table->getTableName());
+ if (op == NULL)
+ {
+ ndbout << "Cannot get operation: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ // TODO: check return value and handle error
+ if (op->writeTuple() == -1)
+ {
+ ndbout << "writeTuple call failed: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ const Uint32 length = (size * arraySize) / 8;
+ if (attr->Desc->m_column->getPrimaryKey())
+ op->equal(i, dataPtr, length);
+ }
+
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ const Uint32 length = (size * arraySize) / 8;
+ if (!attr->Desc->m_column->getPrimaryKey())
+ if (attr->Data.null)
+ op->setValue(i, NULL, 0);
+ else
+ op->setValue(i, dataPtr, length);
+ }
+ int ret = trans->execute(NdbTransaction::Commit);
+ if (ret != 0)
+ {
+ ndbout << "execute failed: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ }
+ m_ndb->closeTransaction(trans);
+ if (ret == 0)
+ break;
+ }
+ m_dataCount++;
+}
+#endif
+
+template class Vector<NdbDictionary::Table*>;
+template class Vector<const NdbDictionary::Table*>;
diff --git a/storage/ndb/tools/restore/consumer_restore.hpp b/storage/ndb/tools/restore/consumer_restore.hpp
new file mode 100644
index 00000000000..1bf6d89a912
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer_restore.hpp
@@ -0,0 +1,93 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef CONSUMER_RESTORE_HPP
+#define CONSUMER_RESTORE_HPP
+
+#include "consumer.hpp"
+
+struct restore_callback_t {
+ class BackupRestore *restore;
+ class TupleS tup;
+ class NdbTransaction *connection;
+ int retries;
+ int error_code;
+ restore_callback_t *next;
+};
+
+
+class BackupRestore : public BackupConsumer
+{
+public:
+ BackupRestore(Uint32 parallelism=1)
+ {
+ m_ndb = 0;
+ m_cluster_connection = 0;
+ m_logCount = m_dataCount = 0;
+ m_restore = false;
+ m_restore_meta = false;
+ m_parallelism = parallelism;
+ m_callback = 0;
+ m_free_callback = 0;
+ m_transactions = 0;
+ m_cache.m_old_table = 0;
+ }
+
+ virtual ~BackupRestore();
+ virtual bool init();
+ virtual void release();
+ virtual bool table(const TableS &);
+ virtual bool endOfTables();
+ virtual void tuple(const TupleS &);
+ virtual void tuple_free();
+ virtual void tuple_a(restore_callback_t *cb);
+ virtual void cback(int result, restore_callback_t *cb);
+ virtual bool errorHandler(restore_callback_t *cb);
+ virtual void exitHandler();
+ virtual void endOfTuples();
+ virtual void logEntry(const LogEntry &);
+ virtual void endOfLogEntrys();
+ virtual bool finalize_table(const TableS &);
+ void connectToMysql();
+ Ndb * m_ndb;
+ Ndb_cluster_connection * m_cluster_connection;
+ bool m_restore;
+ bool m_restore_meta;
+ Uint32 m_logCount;
+ Uint32 m_dataCount;
+
+ Uint32 m_parallelism;
+ volatile Uint32 m_transactions;
+
+ restore_callback_t *m_callback;
+ restore_callback_t *m_free_callback;
+
+ /**
+ * m_new_table_ids[X] = Y;
+ * X - old table id
+ * Y != 0 - new table
+ */
+ Vector<const NdbDictionary::Table*> m_new_tables;
+ struct {
+ const NdbDictionary::Table* m_old_table;
+ const NdbDictionary::Table* m_new_table;
+ } m_cache;
+ const NdbDictionary::Table* get_table(const NdbDictionary::Table* );
+
+ Vector<const NdbDictionary::Table*> m_indexes;
+};
+
+#endif
diff --git a/storage/ndb/tools/restore/consumer_restorem.cpp b/storage/ndb/tools/restore/consumer_restorem.cpp
new file mode 100644
index 00000000000..56179a60ab0
--- /dev/null
+++ b/storage/ndb/tools/restore/consumer_restorem.cpp
@@ -0,0 +1,653 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "consumer_restore.hpp"
+#include <NdbSleep.h>
+
+extern FilteredNdbOut err;
+extern FilteredNdbOut info;
+extern FilteredNdbOut debug;
+
+static bool asynchErrorHandler(NdbTransaction * trans, Ndb * ndb);
+static void callback(int result, NdbTransaction* trans, void* aObject);
+
+bool
+BackupRestore::init()
+{
+
+ if (!m_restore && !m_restore_meta)
+ return true;
+
+ m_ndb = new Ndb();
+
+ if (m_ndb == NULL)
+ return false;
+
+ // Turn off table name completion
+ m_ndb->useFullyQualifiedNames(false);
+
+ m_ndb->init(1024);
+ if (m_ndb->waitUntilReady(30) != 0)
+ {
+ ndbout << "Failed to connect to ndb!!" << endl;
+ return false;
+ }
+ ndbout << "Connected to ndb!!" << endl;
+
+#if USE_MYSQL
+ if(use_mysql)
+ {
+ if ( mysql_thread_safe() == 0 )
+ {
+ ndbout << "Not thread safe mysql library..." << endl;
+ exit(-1);
+ }
+
+ ndbout << "Connecting to MySQL..." <<endl;
+
+ /**
+ * nwe param:
+ * port
+ * host
+ * user
+ */
+ bool returnValue = true;
+ mysql_init(&mysql);
+ {
+ int portNo = 3306;
+ if ( mysql_real_connect(&mysql,
+ ga_host,
+ ga_user,
+ ga_password,
+ ga_database,
+ ga_port,
+:: ga_socket,
+ 0) == NULL )
+ {
+ ndbout_c("Connect failed: %s", mysql_error(&mysql));
+ returnValue = false;
+ }
+ mysql.reconnect= 1;
+ ndbout << "Connected to MySQL!!!" <<endl;
+ }
+
+ /* if(returnValue){
+ mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
+ }
+ */
+ return returnValue;
+ }
+#endif
+
+ if (m_callback) {
+ delete [] m_callback;
+ m_callback = 0;
+ }
+
+ m_callback = new restore_callback_t[m_parallelism];
+
+ if (m_callback == 0)
+ {
+ ndbout << "Failed to allocate callback structs" << endl;
+ return false;
+ }
+
+ m_free_callback = m_callback;
+ for (int i= 0; i < m_parallelism; i++) {
+ m_callback[i].restore = this;
+ m_callback[i].connection = 0;
+ m_callback[i].retries = 0;
+ if (i > 0)
+ m_callback[i-1].next = &(m_callback[i]);
+ }
+ m_callback[m_parallelism-1].next = 0;
+
+ return true;
+
+}
+
+BackupRestore::~BackupRestore()
+{
+ if (m_ndb != 0)
+ delete m_ndb;
+
+ if (m_callback)
+ delete [] m_callback;
+}
+
+#ifdef USE_MYSQL
+bool
+BackupRestore::table(const TableS & table, MYSQL * mysqlp){
+ if (!m_restore_meta)
+ {
+ return true;
+ }
+
+ char tmpTabName[MAX_TAB_NAME_SIZE*2];
+ sprintf(tmpTabName, "%s", table.getTableName());
+ char * database = strtok(tmpTabName, "/");
+ char * schema = strtok( NULL , "/");
+ char * tableName = strtok( NULL , "/");
+
+ /**
+ * this means that the user did not specify schema
+ * and it is a v2x backup
+ */
+ if(database == NULL)
+ return false;
+ if(schema == NULL)
+ return false;
+ if(tableName==NULL)
+ tableName = schema;
+
+ char stmtCreateDB[255];
+ sprintf(stmtCreateDB,"CREATE DATABASE %s", database);
+
+ /*ignore return value. mysql_select_db will trap errors anyways*/
+ if (mysql_query(mysqlp,stmtCreateDB) == 0)
+ {
+ //ndbout_c("%s", stmtCreateDB);
+ }
+
+ if (mysql_select_db(&mysql, database) != 0)
+ {
+ ndbout_c("Error: %s", mysql_error(&mysql));
+ return false;
+ }
+
+ char buf [2048];
+ /**
+ * create table ddl
+ */
+ if (create_table_string(table, tableName, buf))
+ {
+ ndbout_c("Unable to create a table definition since the "
+ "backup contains undefined types");
+ return false;
+ }
+
+ //ndbout_c("%s", buf);
+
+ if (mysql_query(mysqlp,buf) != 0)
+ {
+ ndbout_c("Error: %s", mysql_error(&mysql));
+ return false;
+ } else
+ {
+ ndbout_c("Successfully restored table %s into database %s", tableName, database);
+ }
+
+ return true;
+}
+#endif
+
+bool
+BackupRestore::table(const TableS & table){
+ if (!m_restore_meta)
+ {
+ return true;
+ }
+ NdbDictionary::Dictionary* dict = m_ndb->getDictionary();
+ if (dict->createTable(*table.m_dictTable) == -1)
+ {
+ err << "Create table " << table.getTableName() << " failed: "
+ << dict->getNdbError() << endl;
+ return false;
+ }
+ info << "Successfully restored table " << table.getTableName()<< endl ;
+ return true;
+}
+
+void BackupRestore::tuple(const TupleS & tup)
+{
+ if (!m_restore)
+ {
+ delete &tup;
+ return;
+ }
+
+ restore_callback_t * cb = m_free_callback;
+
+ if (cb)
+ {
+ m_free_callback = cb->next;
+ cb->retries = 0;
+ cb->tup = &tup;
+ tuple_a(cb);
+ }
+
+ if (m_free_callback == 0)
+ {
+ // send-poll all transactions
+ // close transaction is done in callback
+ m_ndb->sendPollNdb(3000, 1);
+ }
+}
+
+void BackupRestore::tuple_a(restore_callback_t *cb)
+{
+ while (cb->retries < 10)
+ {
+ /**
+ * start transactions
+ */
+ cb->connection = m_ndb->startTransaction();
+ if (cb->connection == NULL)
+ {
+ /*
+ if (asynchErrorHandler(cb->connection, m_ndb))
+ {
+ cb->retries++;
+ continue;
+ }
+ */
+ asynchExitHandler();
+ } // if
+
+ const TupleS &tup = *(cb->tup);
+ const TableS * table = tup.getTable();
+ NdbOperation * op = cb->connection->getNdbOperation(table->getTableName());
+
+ if (op == NULL)
+ {
+ if (asynchErrorHandler(cb->connection, m_ndb))
+ {
+ cb->retries++;
+ continue;
+ }
+ asynchExitHandler();
+ } // if
+
+ if (op->writeTuple() == -1)
+ {
+ if (asynchErrorHandler(cb->connection, m_ndb))
+ {
+ cb->retries++;
+ continue;
+ }
+ asynchExitHandler();
+ } // if
+
+ Uint32 ret = 0;
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ char * dataPtr = attr->Data.string_value;
+ Uint32 length = (size * arraySize) / 8;
+ if (attr->Desc->m_column->getPrimaryKey())
+ {
+ ret = op->equal(i, dataPtr, length);
+ }
+ else
+ {
+ if (attr->Data.null)
+ ret = op->setValue(i, NULL, 0);
+ else
+ ret = op->setValue(i, dataPtr, length);
+ }
+
+ if (ret<0)
+ {
+ ndbout_c("Column: %d type %d",i,
+ tup.getTable()->m_dictTable->getColumn(i)->getType());
+ if (asynchErrorHandler(cb->connection, m_ndb))
+ {
+ cb->retries++;
+ break;
+ }
+ asynchExitHandler();
+ }
+ }
+ if (ret < 0)
+ continue;
+
+ // Prepare transaction (the transaction is NOT yet sent to NDB)
+ cb->connection->executeAsynchPrepare(Commit, &callback, cb);
+ m_transactions++;
+ }
+ ndbout_c("Unable to recover from errors. Exiting...");
+ asynchExitHandler();
+}
+
+void BackupRestore::cback(int result, restore_callback_t *cb)
+{
+ if (result<0)
+ {
+ /**
+ * Error. temporary or permanent?
+ */
+ if (asynchErrorHandler(cb->connection, m_ndb))
+ {
+ cb->retries++;
+ tuple_a(cb);
+ }
+ else
+ {
+ ndbout_c("Restore: Failed to restore data "
+ "due to a unrecoverable error. Exiting...");
+ delete m_ndb;
+ delete cb->tup;
+ exit(-1);
+ }
+ }
+ else
+ {
+ /**
+ * OK! close transaction
+ */
+ m_ndb->closeTransaction(cb->connection);
+ delete cb->tup;
+ m_transactions--;
+ }
+}
+
+void BackupRestore::asynchExitHandler()
+{
+ if (m_ndb != NULL)
+ delete m_ndb;
+ exit(-1);
+}
+
+#if 0 // old tuple impl
+void
+BackupRestore::tuple(const TupleS & tup)
+{
+ if (!m_restore)
+ return;
+ while (1)
+ {
+ NdbTransaction * trans = m_ndb->startTransaction();
+ if (trans == NULL)
+ {
+ // Deep shit, TODO: handle the error
+ ndbout << "Cannot start transaction" << endl;
+ exit(-1);
+ } // if
+
+ const TableS * table = tup.getTable();
+ NdbOperation * op = trans->getNdbOperation(table->getTableName());
+ if (op == NULL)
+ {
+ ndbout << "Cannot get operation: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ // TODO: check return value and handle error
+ if (op->writeTuple() == -1)
+ {
+ ndbout << "writeTuple call failed: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ const Uint32 length = (size * arraySize) / 8;
+ if (attr->Desc->m_column->getPrimaryKey())
+ op->equal(i, dataPtr, length);
+ }
+
+ for (int i = 0; i < tup.getNoOfAttributes(); i++)
+ {
+ const AttributeS * attr = tup[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ const Uint32 length = (size * arraySize) / 8;
+ if (!attr->Desc->m_column->getPrimaryKey())
+ if (attr->Data.null)
+ op->setValue(i, NULL, 0);
+ else
+ op->setValue(i, dataPtr, length);
+ }
+ int ret = trans->execute(Commit);
+ if (ret != 0)
+ {
+ ndbout << "execute failed: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ }
+ m_ndb->closeTransaction(trans);
+ if (ret == 0)
+ break;
+ }
+ m_dataCount++;
+}
+#endif
+
+void
+BackupRestore::endOfTuples()
+{
+ if (!m_restore)
+ return;
+
+ // Send all transactions to NDB
+ m_ndb->sendPreparedTransactions(0);
+
+ // Poll all transactions
+ m_ndb->pollNdb(3000, m_transactions);
+
+ // Close all transactions
+ // for (int i = 0; i < nPreparedTransactions; i++)
+ // m_ndb->closeTransaction(asynchTrans[i]);
+}
+
+void
+BackupRestore::logEntry(const LogEntry & tup)
+{
+ if (!m_restore)
+ return;
+
+ NdbTransaction * trans = m_ndb->startTransaction();
+ if (trans == NULL)
+ {
+ // Deep shit, TODO: handle the error
+ ndbout << "Cannot start transaction" << endl;
+ exit(-1);
+ } // if
+
+ const TableS * table = tup.m_table;
+ NdbOperation * op = trans->getNdbOperation(table->getTableName());
+ if (op == NULL)
+ {
+ ndbout << "Cannot get operation: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ } // if
+
+ int check = 0;
+ switch(tup.m_type)
+ {
+ case LogEntry::LE_INSERT:
+ check = op->insertTuple();
+ break;
+ case LogEntry::LE_UPDATE:
+ check = op->updateTuple();
+ break;
+ case LogEntry::LE_DELETE:
+ check = op->deleteTuple();
+ break;
+ default:
+ ndbout << "Log entry has wrong operation type."
+ << " Exiting...";
+ exit(-1);
+ }
+
+ for (int i = 0; i < tup.m_values.size(); i++)
+ {
+ const AttributeS * attr = tup.m_values[i];
+ int size = attr->Desc->size;
+ int arraySize = attr->Desc->arraySize;
+ const char * dataPtr = attr->Data.string_value;
+
+ const Uint32 length = (size / 8) * arraySize;
+ if (attr->Desc->m_column->getPrimaryKey())
+ op->equal(attr->Desc->attrId, dataPtr, length);
+ else
+ op->setValue(attr->Desc->attrId, dataPtr, length);
+ }
+
+#if 1
+ trans->execute(Commit);
+#else
+ const int ret = trans->execute(Commit);
+ // Both insert update and delete can fail during log running
+ // and it's ok
+
+ if (ret != 0)
+ {
+ ndbout << "execute failed: ";
+ ndbout << trans->getNdbError() << endl;
+ exit(-1);
+ }
+#endif
+
+ m_ndb->closeTransaction(trans);
+ m_logCount++;
+}
+
+void
+BackupRestore::endOfLogEntrys()
+{
+ if (m_restore)
+ {
+ ndbout << "Restored " << m_dataCount << " tuples and "
+ << m_logCount << " log entries" << endl;
+ }
+}
+#if 0
+/*****************************************
+ *
+ * Callback function for asynchronous transactions
+ *
+ * Idea for error handling: Transaction objects have to be stored globally when
+ * they are prepared.
+ * In the callback function if the transaction:
+ * succeeded: delete the object from global storage
+ * failed but can be retried: execute the object that is in global storage
+ * failed but fatal: delete the object from global storage
+ *
+ ******************************************/
+static void restoreCallback(int result, // Result for transaction
+ NdbTransaction *object, // Transaction object
+ void *anything) // Not used
+{
+ static Uint32 counter = 0;
+
+
+ debug << "restoreCallback function called " << counter << " time(s)" << endl;
+
+ ++counter;
+
+ if (result == -1)
+ {
+ ndbout << " restoreCallback (" << counter;
+ if ((counter % 10) == 1)
+ {
+ ndbout << "st";
+ } // if
+ else if ((counter % 10) == 2)
+ {
+ ndbout << "nd";
+ } // else if
+ else if ((counter % 10 ) ==3)
+ {
+ ndbout << "rd";
+ } // else if
+ else
+ {
+ ndbout << "th";
+ } // else
+ err << " time: error detected " << object->getNdbError() << endl;
+ } // if
+
+} // restoreCallback
+#endif
+
+
+
+/*
+ * callback : This is called when the transaction is polled
+ *
+ * (This function must have three arguments:
+ * - The result of the transaction,
+ * - The NdbTransaction object, and
+ * - A pointer to an arbitrary object.)
+ */
+
+static void
+callback(int result, NdbTransaction* trans, void* aObject)
+{
+ restore_callback_t *cb = (restore_callback_t *)aObject;
+ (cb->restore)->cback(result, cb);
+}
+
+/**
+ * returns true if is recoverable,
+ * Error handling based on hugo
+ * false if it is an error that generates an abort.
+ */
+static
+bool asynchErrorHandler(NdbTransaction * trans, Ndb* ndb)
+{
+ NdbError error = trans->getNdbError();
+ ndb->closeTransaction(trans);
+ switch(error.status)
+ {
+ case NdbError::Success:
+ return false;
+ // ERROR!
+ break;
+
+ case NdbError::TemporaryError:
+ NdbSleep_MilliSleep(10);
+ return true;
+ // RETRY
+ break;
+
+ case NdbError::UnknownResult:
+ ndbout << error << endl;
+ return false;
+ // ERROR!
+ break;
+
+ default:
+ case NdbError::PermanentError:
+ switch (error.code)
+ {
+ case 499:
+ case 250:
+ NdbSleep_MilliSleep(10);
+ return true; //temp errors?
+ default:
+ break;
+ }
+ //ERROR
+ ndbout << error << endl;
+ return false;
+ break;
+ }
+ return false;
+}
diff --git a/storage/ndb/tools/restore/restore_main.cpp b/storage/ndb/tools/restore/restore_main.cpp
new file mode 100644
index 00000000000..93c40d31adb
--- /dev/null
+++ b/storage/ndb/tools/restore/restore_main.cpp
@@ -0,0 +1,411 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+#include <Vector.hpp>
+#include <ndb_limits.h>
+#include <NdbTCP.h>
+#include <NdbOut.hpp>
+
+#include "consumer_restore.hpp"
+#include "consumer_printer.hpp"
+
+extern FilteredNdbOut err;
+extern FilteredNdbOut info;
+extern FilteredNdbOut debug;
+
+static int ga_nodeId = 0;
+static int ga_nParallelism = 128;
+static int ga_backupId = 0;
+static bool ga_dont_ignore_systab_0 = false;
+static Vector<class BackupConsumer *> g_consumers;
+
+static const char* ga_backupPath = "." DIR_SEPARATOR;
+
+NDB_STD_OPTS_VARS;
+
+/**
+ * print and restore flags
+ */
+static bool ga_restore = false;
+static bool ga_print = false;
+static int _print = 0;
+static int _print_meta = 0;
+static int _print_data = 0;
+static int _print_log = 0;
+static int _restore_data = 0;
+static int _restore_meta = 0;
+
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_restore"),
+ { "connect", 'c', "same as --connect-string",
+ (gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "nodeid", 'n', "Backup files from node with id",
+ (gptr*) &ga_nodeId, (gptr*) &ga_nodeId, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "backupid", 'b', "Backup id",
+ (gptr*) &ga_backupId, (gptr*) &ga_backupId, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "restore_data", 'r',
+ "Restore table data/logs into NDB Cluster using NDBAPI",
+ (gptr*) &_restore_data, (gptr*) &_restore_data, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "restore_meta", 'm',
+ "Restore meta data into NDB Cluster using NDBAPI",
+ (gptr*) &_restore_meta, (gptr*) &_restore_meta, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "parallelism", 'p',
+ "No of parallel transactions during restore of data."
+ "(parallelism can be 1 to 1024)",
+ (gptr*) &ga_nParallelism, (gptr*) &ga_nParallelism, 0,
+ GET_INT, REQUIRED_ARG, 128, 1, 1024, 0, 1, 0 },
+ { "print", 256, "Print data and log to stdout",
+ (gptr*) &_print, (gptr*) &_print, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "print_data", 257, "Print data to stdout",
+ (gptr*) &_print_data, (gptr*) &_print_data, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "print_meta", 258, "Print meta data to stdout",
+ (gptr*) &_print_meta, (gptr*) &_print_meta, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "print_log", 259, "Print log to stdout",
+ (gptr*) &_print_log, (gptr*) &_print_log, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "dont_ignore_systab_0", 'f',
+ "Experimental. Do not ignore system table during restore.",
+ (gptr*) &ga_dont_ignore_systab_0, (gptr*) &ga_dont_ignore_systab_0, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void short_usage_sub(void)
+{
+ printf("Usage: %s [OPTIONS] [<path to backup files>]\n", my_progname);
+}
+static void usage()
+{
+ short_usage_sub();
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_restore.trace";
+#endif
+ ndb_std_get_one_option(optid, opt, argument);
+ switch (optid) {
+ case 'n':
+ if (ga_nodeId == 0)
+ {
+ printf("Error in --nodeid,-n setting, see --help\n");
+ exit(1);
+ }
+ break;
+ case 'b':
+ if (ga_backupId == 0)
+ {
+ printf("Error in --backupid,-b setting, see --help\n");
+ exit(1);
+ }
+ break;
+ }
+ return 0;
+}
+bool
+readArguments(int *pargc, char*** pargv)
+{
+ const char *load_default_groups[]= { "mysql_cluster","ndb_restore",0 };
+ load_defaults("my",load_default_groups,pargc,pargv);
+ if (handle_options(pargc, pargv, my_long_options, get_one_option))
+ {
+ exit(1);
+ }
+
+ BackupPrinter* printer = new BackupPrinter();
+ if (printer == NULL)
+ return false;
+
+ BackupRestore* restore = new BackupRestore(ga_nParallelism);
+ if (restore == NULL)
+ {
+ delete printer;
+ return false;
+ }
+
+ if (_print)
+ {
+ ga_print = true;
+ ga_restore = true;
+ printer->m_print = true;
+ }
+ if (_print_meta)
+ {
+ ga_print = true;
+ printer->m_print_meta = true;
+ }
+ if (_print_data)
+ {
+ ga_print = true;
+ printer->m_print_data = true;
+ }
+ if (_print_log)
+ {
+ ga_print = true;
+ printer->m_print_log = true;
+ }
+
+ if (_restore_data)
+ {
+ ga_restore = true;
+ restore->m_restore = true;
+ }
+
+ if (_restore_meta)
+ {
+ // ga_restore = true;
+ restore->m_restore_meta = true;
+ }
+
+ {
+ BackupConsumer * c = printer;
+ g_consumers.push_back(c);
+ }
+ {
+ BackupConsumer * c = restore;
+ g_consumers.push_back(c);
+ }
+ // Set backup file path
+ if (*pargv[0] != NULL)
+ {
+ ga_backupPath = *pargv[0];
+ }
+
+ return true;
+}
+
+void
+clearConsumers()
+{
+ for(Uint32 i= 0; i<g_consumers.size(); i++)
+ delete g_consumers[i];
+ g_consumers.clear();
+}
+
+static bool
+checkSysTable(const char *tableName)
+{
+ return ga_dont_ignore_systab_0 ||
+ (strcmp(tableName, "SYSTAB_0") != 0 &&
+ strcmp(tableName, "NDB$EVENTS_0") != 0 &&
+ strcmp(tableName, "sys/def/SYSTAB_0") != 0 &&
+ strcmp(tableName, "sys/def/NDB$EVENTS_0") != 0);
+}
+
+static void
+free_data_callback()
+{
+ for(Uint32 i= 0; i < g_consumers.size(); i++)
+ g_consumers[i]->tuple_free();
+}
+
+const char * g_connect_string = 0;
+
+int
+main(int argc, char** argv)
+{
+ NDB_INIT(argv[0]);
+
+ if (!readArguments(&argc, &argv))
+ {
+ return -1;
+ }
+
+ g_connect_string = opt_connect_str;
+
+ /**
+ * we must always load meta data, even if we will only print it to stdout
+ */
+ RestoreMetaData metaData(ga_backupPath, ga_nodeId, ga_backupId);
+ if (!metaData.readHeader())
+ {
+ 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");
+ return -1;
+ }
+
+ if (metaData.getNoOfTables() == 0)
+ {
+ ndbout_c("Restore: The backup contains no tables ");
+ return -1;
+ }
+
+
+ if (!metaData.validateFooter())
+ {
+ ndbout_c("Restore: Failed to validate footer.");
+ return -1;
+ }
+
+ Uint32 i;
+ for(i= 0; i < g_consumers.size(); i++)
+ {
+ if (!g_consumers[i]->init())
+ {
+ clearConsumers();
+ return -11;
+ }
+
+ }
+
+ for(i = 0; i<metaData.getNoOfTables(); i++)
+ {
+ if (checkSysTable(metaData[i]->getTableName()))
+ {
+ for(Uint32 j= 0; j < g_consumers.size(); j++)
+ if (!g_consumers[j]->table(* metaData[i]))
+ {
+ ndbout_c("Restore: Failed to restore table: %s. "
+ "Exiting...",
+ metaData[i]->getTableName());
+ return -11;
+ }
+ }
+ }
+
+ for(i= 0; i < g_consumers.size(); i++)
+ if (!g_consumers[i]->endOfTables())
+ {
+ ndbout_c("Restore: Failed while closing tables");
+ return -11;
+ }
+
+ if (ga_restore || ga_print)
+ {
+ if (ga_restore)
+ {
+ RestoreDataIterator dataIter(metaData, &free_data_callback);
+
+ // Read data file header
+ if (!dataIter.readHeader())
+ {
+ ndbout << "Failed to read header of data file. Exiting..." ;
+ return -11;
+ }
+
+
+ while (dataIter.readFragmentHeader(res= 0))
+ {
+ const TupleS* tuple;
+ while ((tuple = dataIter.getNextTuple(res= 1)) != 0)
+ {
+ if (checkSysTable(tuple->getTable()->getTableName()))
+ for(Uint32 i= 0; i < g_consumers.size(); i++)
+ g_consumers[i]->tuple(* tuple);
+ } // while (tuple != NULL);
+
+ if (res < 0)
+ {
+ ndbout_c("Restore: An error occured while restoring data. "
+ "Exiting...");
+ return -1;
+ }
+ if (!dataIter.validateFragmentFooter()) {
+ ndbout_c("Restore: Error validating fragment footer. "
+ "Exiting...");
+ return -1;
+ }
+ } // while (dataIter.readFragmentHeader(res))
+
+ if (res < 0)
+ {
+ err << "Restore: An error occured while restoring data. Exiting... "
+ << "res=" << res << endl;
+ return -1;
+ }
+
+
+ dataIter.validateFooter(); //not implemented
+
+ for (i= 0; i < g_consumers.size(); i++)
+ g_consumers[i]->endOfTuples();
+
+ RestoreLogIterator logIter(metaData);
+ if (!logIter.readHeader())
+ {
+ err << "Failed to read header of data file. Exiting..." << endl;
+ return -1;
+ }
+
+ const LogEntry * logEntry = 0;
+ while ((logEntry = logIter.getNextLogEntry(res= 0)) != 0)
+ {
+ if (checkSysTable(logEntry->m_table->getTableName()))
+ for(Uint32 i= 0; i < g_consumers.size(); i++)
+ g_consumers[i]->logEntry(* logEntry);
+ }
+ if (res < 0)
+ {
+ err << "Restore: An restoring the data log. Exiting... res="
+ << res << endl;
+ return -1;
+ }
+ logIter.validateFooter(); //not implemented
+ for (i= 0; i < g_consumers.size(); i++)
+ g_consumers[i]->endOfLogEntrys();
+ for(i = 0; i<metaData.getNoOfTables(); i++)
+ {
+ if (checkSysTable(metaData[i]->getTableName()))
+ {
+ for(Uint32 j= 0; j < g_consumers.size(); j++)
+ if (!g_consumers[j]->finalize_table(* metaData[i]))
+ {
+ ndbout_c("Restore: Failed to finalize restore table: %s. "
+ "Exiting...",
+ metaData[i]->getTableName());
+ return -11;
+ }
+ }
+ }
+ }
+ }
+ clearConsumers();
+ return 0;
+} // main
+
+template class Vector<BackupConsumer*>;
diff --git a/storage/ndb/tools/rgrep b/storage/ndb/tools/rgrep
new file mode 100755
index 00000000000..212b068639d
--- /dev/null
+++ b/storage/ndb/tools/rgrep
@@ -0,0 +1,194 @@
+#!/vobs/wds/swt/bin/perl
+
+die "Usage: rgrep [-iredblLn] regexp filepat ...\n rgrep -h for help\n"
+ if $#ARGV < $[;
+
+# Written by Piet van Oostrum <piet@cs.ruu.nl>
+# This is really free software
+
+# Mats Lidell added support for gzip.
+# Mats Lidell added support for skipping line numbers.
+
+$nextopt = 1;
+$igncase = '';
+$regpat = 0;
+$links = 0;
+$error = 0;
+$skipbin = 1;
+$havenl = 1;
+$debug = 0;
+
+do { $regexp = shift (@ARGV); } while &checkopt ($regexp);
+$icreg = $igncase;
+$igncase = '';
+
+eval 'sub grep_file {
+ while (<F>) {
+ $ln++;
+ if (/$regexp/o' . $icreg .') {
+ print "$file:$ln:" if $havenl;
+ print "$_";
+ print "\n" if substr($_, -1, 1) ne "\n";
+ }
+ }
+}';
+
+for (@ARGV) {
+ if (! &checkopt ($_)) {
+ if ($igncase || $regpat || /[?*[]/ || ! -e) {
+ if ($regpat) {
+ s/#/\\#/g;
+ $_ = "#$_#";
+ } else { # translate File pattern into regexp
+ $re = '#($|/)'; $save = $_;
+ while (/[[*?+()|.^$#]/) {
+ $re .= $`;
+ $c = $&;
+ $_ = $';
+ if ($c eq '*') { $c = '[^/]*'; }
+ elsif ($c eq '?') { $c = '[^/]'; }
+ elsif ($c eq '[') {
+ if (/.\]/) { $c = "[$`$&"; $_ = $'; }
+ else {
+ $error++;
+ printf stderr "Illegal filepattern %s\n", $save;
+ }
+ } else { $c = "\\$c"; }
+ $re .= $c;
+ }
+ $_ = "$re$_\$#$igncase";
+ }
+ print "filepat: $_\n" if $debug;
+ push (@filepat, $_);
+ }
+ else { push (@files, $_); print "file: $_\n" if $debug; }
+ }
+}
+
+exit 1 if $errors ;
+
+if ($#filepat < $[) {
+ eval "sub in_pat {1;}" ;
+}
+else {
+ $subtxt = 'sub in_pat { local ($f) = @_;';
+ $or = "";
+ for (@filepat) {
+ $subtxt .= $or . '$f =~ m' . $_;
+ $or = " || ";
+ }
+ $subtxt .= ';};1';
+
+ if (! eval $subtxt) {
+ print $@;
+ exit 1;
+ }
+}
+
+@files = (".") if $#files < $[;
+
+for $file (@files) {
+ &do_grep ($file);
+}
+
+sub do_grep {
+ local ($file) = @_;
+ local (*F, $ln, $f, $g, @dirfiles);
+ if (-f $file) {
+ if (open (F, $file)) {
+ if (-B F) { # binary file -- may be compressed/compacted/gziped
+ if (($cx1 = getc(F)) eq "\377" && (getc(F) eq "\037")) {
+ open (F, "uncompact < $file|");
+ if ($skipbin && -B F) { close (F); return; }
+ }
+ elsif ($cx1 eq "\037" && (($cx2 = getc(F)) eq "\235")) {
+ open (F, "uncompress < $file|");
+ if ($skipbin && -B F) { close (F); return; }
+ }
+ elsif ($cx1 eq "\037" && $cx2 eq "\213") {
+ open (F, "gzip -d < $file|");
+ if ($skipbin && -B F) { close (F); return; }
+ }
+ elsif ($skipbin) {
+ close (F); return;
+ }
+ }
+ print "Reading $file\n" if $debug;
+ &grep_file;
+ } else {
+ print stderr "Cannot open $file\n";
+ }
+ }
+ elsif (-d $file) {
+ print "Entering $file\n" if $debug;
+ if (opendir (F, $file)) {
+ @dirfiles = readdir (F);
+ closedir (F);
+ for $f (@dirfiles) {
+ next if ($f eq '.' || $f eq '..');
+ $g = "$file/$f";
+ next if (-l $g && ($links < 1 || $links == 1 && -d $g));
+ if (-f $g && &in_pat ($g) || -d _) {
+ &do_grep ($g);
+ }
+ }
+ } else {
+ print stderr "Can't open $file\n";
+ }
+ }
+}
+
+sub checkopt {
+ local ($_) = $_[0];
+ if (/^-/ && $nextopt) {
+ $nextopt = 1;
+ @opt = split (/-*/,$_); shift (@opt);
+ for $opt (@opt) {
+ if ($opt eq 'i') { $igncase = 'i'; }
+ elsif ($opt eq 'd') { $debug = 1; }
+ elsif ($opt eq 'l') { $links = 1; }
+ elsif ($opt eq 'L') { $links = 2; }
+ elsif ($opt eq 'b') { $skipbin = 0; }
+ elsif ($opt eq 'r') { $regpat = 1; }
+ elsif ($opt eq 'e') { $nextopt = 0; }
+ elsif ($opt eq 'n') { $havenl = 0; }
+ elsif ($opt eq 'h' || $opt eq 'H') { & help; }
+ else { $error++; printf stderr "Unknown option -%s\n", $opt; }
+ }
+ return 1;
+ }
+ $nextopt = 1;
+ return 0;
+}
+
+sub help {
+ print <<'HELP'; exit 0;
+Usage: rgrep [-iredblL] regexp filepat ...
+ regexp = perl regular expression to search
+ filepat ... = a list of files and directories to be searched or
+ file patterns to match filenames.
+ filepat will be interpreted as file or directory name if it exists
+ as such, and does not contain the metacharacters [ ] ? or *. After
+ the options -i and -r all filepats will be considered patterns.
+ rgrep will search all files in any of the directories given (and its
+ subdirectories) that match any of the filepats, except binary files.
+ Compressed files will be searched in uncompressed form.
+ Note: filepats may contain / contrary to find usage.
+ -b Don't skip binary files.
+ -i Ignore case, either in the regexp or in filename matching (depending
+ on the location). Before the regexp only applies to the regexp,
+ otherwise to the filepats following it.
+ -r The following filepats are treated as real perl regexps rather than
+ shell style filename patterns. In this case / is not a special
+ character, i.e. it is matched by . and matching is not anchored (you
+ must supply ^ and $ yourself). E.g. a.b matches the file /xa/by/zz.
+ -l Do follow symbolic links only for files (default is do not follow).
+ -L Do follow symbolic links for files and directories.
+ -e Do not interpret following argument as option. Useful if regexp or
+ filepat starts with a -.
+ -d Debugging: Give a lot of output on what happens.
+ -n Don't precede each line by its relative line number in the file.
+ -h print this message and exit.
+Piet van Oostrum <piet@cs.ruu.nl>
+HELP
+}
diff --git a/storage/ndb/tools/select_all.cpp b/storage/ndb/tools/select_all.cpp
new file mode 100644
index 00000000000..baa18db1ebd
--- /dev/null
+++ b/storage/ndb/tools/select_all.cpp
@@ -0,0 +1,358 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+
+int scanReadRecords(Ndb*,
+ const NdbDictionary::Table*,
+ const NdbDictionary::Index*,
+ int parallel,
+ int lockType,
+ bool headers,
+ bool useHexFormat,
+ char delim,
+ bool orderby,
+ bool descending);
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static const char* _delimiter = "\t";
+static int _unqualified, _header, _parallelism, _useHexFormat, _lock,
+ _order, _descending;
+
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "parallelism", 'p', "parallelism",
+ (gptr*) &_parallelism, (gptr*) &_parallelism, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "lock", 'l', "Read(0), Read-hold(1), Exclusive(2)",
+ (gptr*) &_lock, (gptr*) &_lock, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "order", 'o', "Sort resultset according to index",
+ (gptr*) &_order, (gptr*) &_order, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "descending", 'z', "Sort descending (requires order flag)",
+ (gptr*) &_descending, (gptr*) &_descending, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "header", 'h', "Print header",
+ (gptr*) &_header, (gptr*) &_header, 0,
+ GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
+ { "useHexFormat", 'x', "Output numbers in hexadecimal format",
+ (gptr*) &_useHexFormat, (gptr*) &_useHexFormat, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "delimiter", 'D', "Column delimiter",
+ (gptr*) &_delimiter, (gptr*) &_delimiter, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname\n"\
+ "This program reads all records from one table in NDB Cluster\n"\
+ "and print them to stdout. This is performed using a scan read.\n"\
+ "(It only print error messages if it encounters a permanent error.)\n"\
+ "It can also be used to dump the content of a table to file \n"\
+ " ex: select_all --no-header --delimiter=';' T4 > T4.data\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ const char* _tabname;
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_select_all.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ if ((_tabname = argv[0]) == 0) {
+ usage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname );
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Check if table exists in db
+ const NdbDictionary::Table* pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ const NdbDictionary::Index * pIdx = 0;
+ if(argc > 1){
+ pIdx = MyNdb.getDictionary()->getIndex(argv[1], _tabname);
+ }
+
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if(argc > 1 && pIdx == 0)
+ {
+ ndbout << " Index " << argv[1] << " does not exists" << endl;
+ }
+
+ if(_order && pIdx == NULL){
+ ndbout << " Order flag given without an index" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if (_descending && ! _order) {
+ ndbout << " Descending flag given without order flag" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if (scanReadRecords(&MyNdb,
+ pTab,
+ pIdx,
+ _parallelism,
+ _lock,
+ _header,
+ _useHexFormat,
+ (char)*_delimiter, _order, _descending) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+int scanReadRecords(Ndb* pNdb,
+ const NdbDictionary::Table* pTab,
+ const NdbDictionary::Index* pIdx,
+ int parallel,
+ int _lock,
+ bool headers,
+ bool useHexFormat,
+ char delimiter, bool order, bool descending){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbTransaction *pTrans;
+ NdbScanOperation *pOp;
+ NdbIndexScanOperation * pIOp= 0;
+
+ NDBT_ResultRow * row = new NDBT_ResultRow(*pTab, delimiter);
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ ndbout << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return -1;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return -1;
+ }
+
+
+ pOp = (!pIdx) ? pTrans->getNdbScanOperation(pTab->getName()) :
+ pIOp=pTrans->getNdbIndexScanOperation(pIdx->getName(), pTab->getName());
+
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ int rs;
+ switch(_lock + (3 * order)){
+ case 1:
+ rs = pOp->readTuples(NdbScanOperation::LM_Read, 0, parallel);
+ break;
+ case 2:
+ rs = pOp->readTuples(NdbScanOperation::LM_Exclusive, 0, parallel);
+ break;
+ case 3:
+ rs = pIOp->readTuples(NdbScanOperation::LM_CommittedRead, 0, parallel,
+ true, descending);
+ break;
+ case 4:
+ rs = pIOp->readTuples(NdbScanOperation::LM_Read, 0, parallel, true, descending);
+ break;
+ case 5:
+ rs = pIOp->readTuples(NdbScanOperation::LM_Exclusive, 0, parallel, true, descending);
+ break;
+ case 0:
+ default:
+ rs = pOp->readTuples(NdbScanOperation::LM_CommittedRead, 0, parallel);
+ break;
+ }
+ if( rs != 0 ){
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ if(0){
+ NdbScanFilter sf(pOp);
+#if 0
+ sf.begin(NdbScanFilter::AND);
+ sf.le(0, (Uint32)10);
+
+ sf.end();
+#elif 0
+ sf.begin(NdbScanFilter::OR);
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)10);
+ sf.lt(0, (Uint32)20);
+ sf.end();
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)30);
+ sf.lt(0, (Uint32)40);
+ sf.end();
+ sf.end();
+#elif 1
+ sf.begin(NdbScanFilter::AND);
+ sf.begin(NdbScanFilter::OR);
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)10);
+ sf.lt(0, (Uint32)20);
+ sf.end();
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)30);
+ sf.lt(0, (Uint32)40);
+ sf.end();
+ sf.end();
+ sf.begin(NdbScanFilter::OR);
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)0);
+ sf.lt(0, (Uint32)50);
+ sf.end();
+ sf.begin(NdbScanFilter::AND);
+ sf.ge(0, (Uint32)100);
+ sf.lt(0, (Uint32)200);
+ sf.end();
+ sf.end();
+ sf.end();
+#endif
+ } else {
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+ }
+
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if((row->attributeStore(a) =
+ pOp->getValue(pTab->getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+ }
+
+ check = pTrans->execute(NdbTransaction::NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ if (headers)
+ row->header(ndbout) << endl;
+
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+
+ if (useHexFormat) {
+ ndbout.setHexFormat(1) << (*row) << endl;
+ } else {
+ ndbout << (*row) << endl;
+ }
+
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ ndbout << rows << " rows returned" << endl;
+
+ return 0;
+ }
+ return -1;
+}
diff --git a/storage/ndb/tools/select_count.cpp b/storage/ndb/tools/select_count.cpp
new file mode 100644
index 00000000000..6fa3c77f15a
--- /dev/null
+++ b/storage/ndb/tools/select_count.cpp
@@ -0,0 +1,212 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <UtilTransactions.hpp>
+
+static int
+select_count(Ndb* pNdb, const NdbDictionary::Table* pTab,
+ int parallelism,
+ int* count_rows,
+ NdbOperation::LockMode lock);
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static int _parallelism = 240;
+static int _lock = 0;
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "parallelism", 'p', "parallelism",
+ (gptr*) &_parallelism, (gptr*) &_parallelism, 0,
+ GET_INT, REQUIRED_ARG, 240, 0, 0, 0, 0, 0 },
+ { "lock", 'l', "Read(0), Read-hold(1), Exclusive(2)",
+ (gptr*) &_lock, (gptr*) &_lock, 0,
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void usage()
+{
+ char desc[] =
+ "tabname1 ... tabnameN\n"\
+ "This program will count the number of records in tables\n";
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_select_count.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ if (argc < 1) {
+ usage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ if (con.wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname );
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ for(int i = 0; i<argc; i++){
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, argv[i]);
+ if(pTab == NULL){
+ ndbout << " Table " << argv[i] << " does not exist!" << endl;
+ continue;
+ }
+
+ int rows = 0;
+ if (select_count(&MyNdb, pTab, _parallelism, &rows,
+ (NdbOperation::LockMode)_lock) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ ndbout << rows << " records in table " << argv[i] << endl;
+ }
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+int
+select_count(Ndb* pNdb, const NdbDictionary::Table* pTab,
+ int parallelism,
+ int* count_rows,
+ NdbOperation::LockMode lock){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbTransaction *pTrans;
+ NdbScanOperation *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){
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ pOp = pTrans->getNdbScanOperation(pTab->getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Dirty) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+
+ check = pOp->interpret_exit_last_row();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ 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(NdbTransaction::NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint64 row_count = 0;
+ int eof;
+ while((eof = pOp->nextResult(true)) == 0){
+ row_count += tmp;
+ }
+
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ if (count_rows != NULL){
+ *count_rows = row_count;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+
diff --git a/storage/ndb/tools/waiter.cpp b/storage/ndb/tools/waiter.cpp
new file mode 100644
index 00000000000..db90bd8bd90
--- /dev/null
+++ b/storage/ndb/tools/waiter.cpp
@@ -0,0 +1,314 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <mgmapi.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <kernel/ndb_limits.h>
+
+#include <NDBT.hpp>
+
+int
+waitClusterStatus(const char* _addr, ndb_mgm_node_status _status,
+ unsigned int _timeout);
+
+enum ndb_waiter_options {
+ OPT_WAIT_STATUS_NOT_STARTED = NDB_STD_OPTIONS_LAST
+};
+NDB_STD_OPTS_VARS;
+
+static int _no_contact = 0;
+static int _not_started = 0;
+static int _timeout = 120;
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_desc"),
+ { "no-contact", 'n', "Wait for cluster no contact",
+ (gptr*) &_no_contact, (gptr*) &_no_contact, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "not-started", OPT_WAIT_STATUS_NOT_STARTED, "Wait for cluster not started",
+ (gptr*) &_not_started, (gptr*) &_not_started, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "timeout", 't', "Timeout to wait",
+ (gptr*) &_timeout, (gptr*) &_timeout, 0,
+ GET_INT, REQUIRED_ARG, 120, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void usage()
+{
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ const char* _hostName = NULL;
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_waiter.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ _hostName = argv[0];
+
+ if (_hostName == 0)
+ _hostName= opt_connect_str;
+
+ enum ndb_mgm_node_status wait_status;
+ if (_no_contact)
+ {
+ wait_status= NDB_MGM_NODE_STATUS_NO_CONTACT;
+ }
+ else if (_not_started)
+ {
+ wait_status= NDB_MGM_NODE_STATUS_NOT_STARTED;
+ }
+ else
+ {
+ wait_status= NDB_MGM_NODE_STATUS_STARTED;
+ }
+
+ if (waitClusterStatus(_hostName, wait_status, _timeout) != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+#define MGMERR(h) \
+ ndbout << "latest_error="<<ndb_mgm_get_latest_error(h) \
+ << ", line="<<ndb_mgm_get_latest_error_line(h) \
+ << endl;
+
+NdbMgmHandle handle= NULL;
+
+Vector<ndb_mgm_node_state> ndbNodes;
+Vector<ndb_mgm_node_state> mgmNodes;
+Vector<ndb_mgm_node_state> apiNodes;
+
+int
+getStatus(){
+ int retries = 0;
+ struct ndb_mgm_cluster_state * status;
+ struct ndb_mgm_node_state * node;
+
+ ndbNodes.clear();
+ mgmNodes.clear();
+ apiNodes.clear();
+
+ while(retries < 10){
+ status = ndb_mgm_get_status(handle);
+ if (status == NULL){
+ ndbout << "status==NULL, retries="<<retries<<endl;
+ MGMERR(handle);
+ retries++;
+ continue;
+ }
+ int count = status->no_of_nodes;
+ for (int i = 0; i < count; i++){
+ node = &status->node_states[i];
+ switch(node->node_type){
+ case NDB_MGM_NODE_TYPE_NDB:
+ ndbNodes.push_back(*node);
+ break;
+ case NDB_MGM_NODE_TYPE_MGM:
+ mgmNodes.push_back(*node);
+ break;
+ case NDB_MGM_NODE_TYPE_API:
+ apiNodes.push_back(*node);
+ break;
+ default:
+ if(node->node_status == NDB_MGM_NODE_STATUS_UNKNOWN ||
+ node->node_status == NDB_MGM_NODE_STATUS_NO_CONTACT){
+ retries++;
+ ndbNodes.clear();
+ mgmNodes.clear();
+ apiNodes.clear();
+ free(status);
+ status = NULL;
+ count = 0;
+
+ ndbout << "kalle"<< endl;
+ break;
+ }
+ abort();
+ break;
+ }
+ }
+ if(status == 0){
+ ndbout << "status == 0" << endl;
+ continue;
+ }
+ free(status);
+ return 0;
+ }
+
+ g_err << "getStatus failed" << endl;
+ return -1;
+}
+
+int
+waitClusterStatus(const char* _addr,
+ ndb_mgm_node_status _status,
+ unsigned int _timeout)
+{
+ int _startphase = -1;
+
+ int _nodes[MAX_NDB_NODES];
+ int _num_nodes = 0;
+
+ handle = ndb_mgm_create_handle();
+ if (handle == NULL){
+ g_err << "handle == NULL" << endl;
+ return -1;
+ }
+ g_info << "Connecting to mgmsrv at " << _addr << endl;
+ if (ndb_mgm_set_connectstring(handle, _addr))
+ {
+ MGMERR(handle);
+ g_err << "Connectstring " << _addr << " invalid" << endl;
+ return -1;
+ }
+ if (ndb_mgm_connect(handle,0,0,1)) {
+ MGMERR(handle);
+ g_err << "Connection to " << _addr << " failed" << endl;
+ return -1;
+ }
+
+ if (getStatus() != 0)
+ return -1;
+
+ // Collect all nodes into nodes
+ for (size_t i = 0; i < ndbNodes.size(); i++){
+ _nodes[i] = ndbNodes[i].node_id;
+ _num_nodes++;
+ }
+
+ unsigned int attempts = 0;
+ unsigned int resetAttempts = 0;
+ const unsigned int MAX_RESET_ATTEMPTS = 10;
+ bool allInState = false;
+ while (allInState == false){
+ if (_timeout > 0 && attempts > _timeout){
+ /**
+ * Timeout has expired waiting for the nodes to enter
+ * the state we want
+ */
+ bool waitMore = false;
+ /**
+ * Make special check if we are waiting for
+ * cluster to become started
+ */
+ if(_status == NDB_MGM_NODE_STATUS_STARTED){
+ waitMore = true;
+ /**
+ * First check if any node is not starting
+ * then it's no idea to wait anymore
+ */
+ for (size_t n = 0; n < ndbNodes.size(); n++){
+ if (ndbNodes[n].node_status != NDB_MGM_NODE_STATUS_STARTED &&
+ ndbNodes[n].node_status != NDB_MGM_NODE_STATUS_STARTING)
+ waitMore = false;
+
+ }
+ }
+
+ if (!waitMore || resetAttempts > MAX_RESET_ATTEMPTS){
+ g_err << "waitNodeState("
+ << ndb_mgm_get_node_status_string(_status)
+ <<", "<<_startphase<<")"
+ << " timeout after " << attempts <<" attemps" << endl;
+ return -1;
+ }
+
+ g_err << "waitNodeState("
+ << ndb_mgm_get_node_status_string(_status)
+ <<", "<<_startphase<<")"
+ << " resetting number of attempts "
+ << resetAttempts << endl;
+ attempts = 0;
+ resetAttempts++;
+
+ }
+
+ allInState = true;
+ if (getStatus() != 0){
+ g_err << "getStatus != 0" << endl;
+ return -1;
+ }
+
+ // ndbout << "waitNodeState; _num_nodes = " << _num_nodes << endl;
+ // for (int i = 0; i < _num_nodes; i++)
+ // ndbout << " node["<<i<<"] =" <<_nodes[i] << endl;
+
+ for (int i = 0; i < _num_nodes; i++){
+ ndb_mgm_node_state* ndbNode = NULL;
+ for (size_t n = 0; n < ndbNodes.size(); n++){
+ if (ndbNodes[n].node_id == _nodes[i])
+ ndbNode = &ndbNodes[n];
+ }
+
+ if(ndbNode == NULL){
+ allInState = false;
+ continue;
+ }
+
+ g_info << "State node " << ndbNode->node_id << " "
+ << ndb_mgm_get_node_status_string(ndbNode->node_status)<< endl;
+
+ assert(ndbNode != NULL);
+
+ if(_status == NDB_MGM_NODE_STATUS_STARTING &&
+ ((ndbNode->node_status == NDB_MGM_NODE_STATUS_STARTING &&
+ ndbNode->start_phase >= _startphase) ||
+ (ndbNode->node_status == NDB_MGM_NODE_STATUS_STARTED)))
+ continue;
+
+ if (_status == NDB_MGM_NODE_STATUS_STARTING){
+ g_info << "status = "
+ << ndb_mgm_get_node_status_string(ndbNode->node_status)
+ <<", start_phase="<<ndbNode->start_phase<<endl;
+ if (ndbNode->node_status != _status) {
+ if (ndbNode->node_status < _status)
+ allInState = false;
+ else
+ g_info << "node_status(" << (unsigned)ndbNode->node_status
+ << ") != _status("<< (unsigned)_status << ")" <<endl;
+ } else if (ndbNode->start_phase < _startphase)
+ allInState = false;
+ } else {
+ if (ndbNode->node_status != _status)
+ allInState = false;
+ }
+ }
+ g_info << "Waiting for cluster enter state "
+ << ndb_mgm_get_node_status_string(_status)<< endl;
+ NdbSleep_SecSleep(1);
+ attempts++;
+ }
+ return 0;
+}
+
+template class Vector<ndb_mgm_node_state>;