diff options
Diffstat (limited to 'storage/ndb/tools')
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(§ionInfo, 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(§ionInfo, 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>; |