summaryrefslogtreecommitdiff
path: root/cpp/src/FreezeScript/transformdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/FreezeScript/transformdb.cpp')
-rw-r--r--cpp/src/FreezeScript/transformdb.cpp981
1 files changed, 981 insertions, 0 deletions
diff --git a/cpp/src/FreezeScript/transformdb.cpp b/cpp/src/FreezeScript/transformdb.cpp
new file mode 100644
index 00000000000..117f84aa600
--- /dev/null
+++ b/cpp/src/FreezeScript/transformdb.cpp
@@ -0,0 +1,981 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <FreezeScript/Transformer.h>
+#include <FreezeScript/TransformAnalyzer.h>
+#include <FreezeScript/Exception.h>
+#include <FreezeScript/Util.h>
+#include <Freeze/Initialize.h>
+#include <Freeze/Transaction.h>
+#include <Freeze/Catalog.h>
+#include <IceUtil/Options.h>
+#include <IceUtil/FileUtil.h>
+#include <db_cxx.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+using namespace std;
+
+#ifdef _WIN32
+# define FREEZE_SCRIPT_DB_MODE 0
+#else
+# define FREEZE_SCRIPT_DB_MODE (S_IRUSR | S_IWUSR)
+#endif
+
+static void
+usage(const std::string& n)
+{
+ cerr << "Usage:\n";
+ cerr << "\n";
+ cerr << n << " -o FILE [-i] [slice-options] [type-options]\n";
+ cerr << " Generates descriptors in FILE for a database.\n";
+ cerr << "\n";
+ cerr << n << " -o FILE [-i] [slice-options] DBENV\n";
+ cerr << " Generates descriptors in FILE for all databases in the environment DBENV.\n";
+ cerr << "\n";
+ cerr << n << " [options] [slice-options] [type-options] DBENV DB NEWDBENV\n";
+ cerr << " Transform the database DB in the environment DBENV. A database of the\n";
+ cerr << " same name is created in the environment NEWDBENV.\n";
+ cerr << "\n";
+ cerr << n << " [options] [slice-options] DBENV NEWDBENV\n";
+ cerr << " Transform all databases from the environment DBENV into the\n";
+ cerr << " environment NEWDBENV.\n";
+ cerr << "\n";
+ cerr <<
+ "Slice Options:\n"
+ "-DNAME Define NAME as 1.\n"
+ "-DNAME=DEF Define NAME as DEF.\n"
+ "-UNAME Remove any definition for NAME.\n"
+ "-d, --debug Print debug messages.\n"
+ "--underscore Permit underscores in Slice identifiers.\n"
+ "--include-old DIR Put DIR in the include file search path for old Slice\n"
+ " definitions.\n"
+ "--include-new DIR Put DIR in the include file search path for new Slice\n"
+ " definitions.\n"
+ "--old SLICE Load old Slice definitions from the file SLICE.\n"
+ "--new SLICE Load new Slice definitions from the file SLICE.\n"
+ ;
+ cerr << "\n";
+ cerr <<
+ "Type Options:\n"
+ "-e Indicates the database is an Evictor database.\n"
+ "--key TYPE[,TYPE] Specifies the Slice types of the database key. If the\n"
+ " type names have not changed, only one needs to be\n"
+ " specified. Otherwise, the type names are specified as\n"
+ " old-type,new-type.\n"
+ "--value TYPE[,TYPE] Specifies the Slice types of the database value. If the\n"
+ " type names have not changed, only one needs to be\n"
+ " specified. Otherwise, the type names are specified as\n"
+ " old-type,new-type.\n"
+ ;
+ cerr << "\n";
+ cerr <<
+ "Options:\n"
+ "-h, --help Show this message.\n"
+ "-v, --version Display the Ice version.\n"
+ "-i Ignore incompatible type changes.\n"
+ "-p Purge objects whose types no longer exist.\n"
+ "-c Use catastrophic recovery on the old database environment.\n"
+ "-w Suppress duplicate warnings during migration.\n"
+ "-f FILE Execute the transformation descriptors in the file FILE.\n"
+ ;
+}
+
+static Slice::TypePtr
+findType(const Slice::UnitPtr& u, const string& type)
+{
+ Slice::TypeList l;
+
+ string t;
+ if(type == "::Ice::Object")
+ {
+ t = "Object";
+ }
+ else if(type == "::Ice::Object*")
+ {
+ t = "Object*";
+ }
+ else
+ {
+ t = type;
+ }
+
+ l = u->lookupType(t, false);
+ if(l.empty())
+ {
+ return 0;
+ }
+
+ return l.front();
+}
+
+static void
+transformDb(bool evictor, const Ice::CommunicatorPtr& communicator,
+ const FreezeScript::ObjectFactoryPtr& objectFactory,
+ DbEnv& dbEnv, DbEnv& dbEnvNew, const string& dbName,
+ const Freeze::ConnectionPtr& connectionNew, vector<Db*>& dbs,
+ const Slice::UnitPtr& oldUnit, const Slice::UnitPtr& newUnit,
+ DbTxn* txnNew, bool purgeObjects, bool suppress, string descriptors)
+{
+ if(evictor)
+ {
+ //
+ // The evictor database file contains multiple databases. We must first
+ // determine the names of those databases, ignoring any whose names
+ // begin with "$index:". Each database represents a separate facet, with
+ // the facet name used as the database name. The database named "$default"
+ // represents the main object.
+ //
+ vector<string> dbNames;
+ {
+ Db db(&dbEnv, 0);
+ db.open(0, dbName.c_str(), 0, DB_UNKNOWN, DB_RDONLY, 0);
+ Dbt dbKey, dbValue;
+ dbKey.set_flags(DB_DBT_MALLOC);
+ dbValue.set_flags(DB_DBT_USERMEM | DB_DBT_PARTIAL);
+
+ Dbc* dbc = 0;
+ db.cursor(0, &dbc, 0);
+
+ while(dbc->get(&dbKey, &dbValue, DB_NEXT) == 0)
+ {
+ string s(static_cast<char*>(dbKey.get_data()), dbKey.get_size());
+ if(s.find("$index:") != 0)
+ {
+ dbNames.push_back(s);
+ }
+ free(dbKey.get_data());
+ }
+
+ dbc->close();
+ db.close(0);
+ }
+
+ //
+ // Transform each database. We must delay closing the new databases
+ // until after the transaction is committed or aborted.
+ //
+ for(vector<string>::iterator p = dbNames.begin(); p != dbNames.end(); ++p)
+ {
+ string name = p->c_str();
+
+ Db db(&dbEnv, 0);
+ db.open(0, dbName.c_str(), name.c_str(), DB_BTREE, DB_RDONLY, FREEZE_SCRIPT_DB_MODE);
+
+ Db* dbNew = new Db(&dbEnvNew, 0);
+ dbs.push_back(dbNew);
+ dbNew->open(txnNew, dbName.c_str(), name.c_str(), DB_BTREE, DB_CREATE | DB_EXCL, FREEZE_SCRIPT_DB_MODE);
+
+ //
+ // Execute the transformation descriptors.
+ //
+ istringstream istr(descriptors);
+ string facet = (name == "$default" ? string("") : name);
+ FreezeScript::transformDatabase(communicator, objectFactory, oldUnit, newUnit, &db, dbNew, txnNew, 0,
+ dbName, facet, purgeObjects, cerr, suppress, istr);
+
+ db.close(0);
+ }
+
+ Freeze::Catalog catalogNew(connectionNew, Freeze::catalogName());
+ Freeze::CatalogData catalogData;
+ catalogData.evictor = true;
+ catalogNew.put(Freeze::Catalog::value_type(dbName, catalogData));
+ }
+ else
+ {
+ //
+ // Transform a map database.
+ //
+ Db db(&dbEnv, 0);
+ db.open(0, dbName.c_str(), 0, DB_BTREE, DB_RDONLY, FREEZE_SCRIPT_DB_MODE);
+
+ Db* dbNew = new Db(&dbEnvNew, 0);
+ dbs.push_back(dbNew);
+ dbNew->open(txnNew, dbName.c_str(), 0, DB_BTREE, DB_CREATE | DB_EXCL, FREEZE_SCRIPT_DB_MODE);
+
+ //
+ // Execute the transformation descriptors.
+ //
+ istringstream istr(descriptors);
+ FreezeScript::transformDatabase(communicator, objectFactory, oldUnit, newUnit, &db, dbNew, txnNew,
+ connectionNew, dbName, "", purgeObjects, cerr, suppress, istr);
+
+ db.close(0);
+ }
+}
+
+static int
+run(const Ice::StringSeq& originalArgs, const Ice::CommunicatorPtr& communicator)
+{
+ vector<string> oldCppArgs;
+ vector<string> newCppArgs;
+ bool debug;
+ bool ice = true; // Needs to be true in order to create default definitions.
+ bool underscore;
+ string outputFile;
+ bool ignoreTypeChanges;
+ bool purgeObjects;
+ bool catastrophicRecover;
+ bool suppress;
+ string inputFile;
+ vector<string> oldSlice;
+ vector<string> newSlice;
+ bool evictor;
+ string keyTypeNames;
+ string valueTypeNames;
+ string dbEnvName, dbName, dbEnvNameNew;
+ bool allDb = false;
+
+ IceUtilInternal::Options opts;
+ opts.addOpt("h", "help");
+ opts.addOpt("v", "version");
+ opts.addOpt("D", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("U", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("d", "debug");
+ opts.addOpt("", "underscore");
+ opts.addOpt("o", "", IceUtilInternal::Options::NeedArg);
+ opts.addOpt("i");
+ opts.addOpt("p");
+ opts.addOpt("c");
+ opts.addOpt("w");
+ opts.addOpt("f", "", IceUtilInternal::Options::NeedArg);
+ opts.addOpt("", "include-old", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("", "include-new", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("", "old", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("", "new", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
+ opts.addOpt("a");
+ opts.addOpt("e");
+ opts.addOpt("", "key", IceUtilInternal::Options::NeedArg);
+ opts.addOpt("", "value", IceUtilInternal::Options::NeedArg);
+
+ const string appName = originalArgs[0];
+ vector<string> args;
+ try
+ {
+ args = opts.parse(originalArgs);
+ }
+ catch(const IceUtilInternal::BadOptException& e)
+ {
+ cerr << appName << ": " << e.reason << endl;
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+
+ if(opts.isSet("help"))
+ {
+ usage(appName);
+ return EXIT_SUCCESS;
+ }
+ if(opts.isSet("version"))
+ {
+ cout << ICE_STRING_VERSION << endl;
+ return EXIT_SUCCESS;
+ }
+ if(opts.isSet("D"))
+ {
+ vector<string> optargs = opts.argVec("D");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ oldCppArgs.push_back("-D" + *i);
+ newCppArgs.push_back("-D" + *i);
+ }
+ }
+ if(opts.isSet("U"))
+ {
+ vector<string> optargs = opts.argVec("U");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ oldCppArgs.push_back("-U" + *i);
+ newCppArgs.push_back("-U" + *i);
+ }
+ }
+ debug = opts.isSet("debug");
+
+ underscore = opts.isSet("underscore");
+
+ if(opts.isSet("o"))
+ {
+ outputFile = opts.optArg("o");
+ }
+ ignoreTypeChanges = opts.isSet("i");
+ purgeObjects = opts.isSet("p");
+ catastrophicRecover = opts.isSet("c");
+ suppress = opts.isSet("w");
+ if(opts.isSet("f"))
+ {
+ inputFile = opts.optArg("f");
+ }
+ if(opts.isSet("include-old"))
+ {
+ vector<string> optargs = opts.argVec("include-old");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ oldCppArgs.push_back("-I" + *i);
+ }
+ }
+ if(opts.isSet("include-new"))
+ {
+ vector<string> optargs = opts.argVec("include-new");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ newCppArgs.push_back("-I" + *i);
+ }
+ }
+ if(opts.isSet("old"))
+ {
+ vector<string> optargs = opts.argVec("old");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ oldSlice.push_back(*i);
+ }
+ }
+ if(opts.isSet("new"))
+ {
+ vector<string> optargs = opts.argVec("new");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ newSlice.push_back(*i);
+ }
+ }
+ evictor = opts.isSet("e");
+ if(opts.isSet("key"))
+ {
+ keyTypeNames = opts.optArg("key");
+ }
+ if(opts.isSet("value"))
+ {
+ valueTypeNames = opts.optArg("value");
+ }
+
+ if(outputFile.empty())
+ {
+ if(args.size() == 2)
+ {
+ allDb = true;
+ }
+ else if(args.size() != 3)
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ if(args.size() == 1)
+ {
+ allDb = true;
+ }
+ else if(args.size() != 0)
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if(allDb && (!keyTypeNames.empty() || !valueTypeNames.empty()))
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+
+ if(inputFile.empty() && !allDb && !evictor && (keyTypeNames.empty() || valueTypeNames.empty()))
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+
+ if(args.size() > 0)
+ {
+ dbEnvName = args[0];
+ }
+ if(args.size() > 1)
+ {
+ if(allDb)
+ {
+ dbEnvNameNew = args[1];
+ }
+ else
+ {
+ dbName = args[1];
+ }
+ }
+ if(args.size() > 2)
+ {
+ dbEnvNameNew = args[2];
+ }
+ if(args.size() > 3)
+ {
+ cerr << appName << ": too many arguments" << endl;
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+
+ //
+ // Freeze creates a lock file by default to prevent multiple processes from opening
+ // the same database environment simultaneously. In the case of a read-only program
+ // such as transformdb, however, we still want to be able to open the environment despite
+ // the lock. This assumes of course that the other process has opened the environment
+ // with DbPrivate=0. If DbPrivate=0 is also set for dumpdb, we disable the lock.
+ //
+ if(!catastrophicRecover && outputFile.empty())
+ {
+ Ice::PropertiesPtr props = communicator->getProperties();
+ string prefix = "Freeze.DbEnv." + args[0];
+ if(props->getPropertyAsIntWithDefault(prefix + ".DbPrivate", 1) == 0)
+ {
+ props->setProperty(prefix + ".LockFile", "0");
+ }
+ }
+
+ Slice::UnitPtr oldUnit = Slice::Unit::createUnit(true, true, ice, underscore);
+ FreezeScript::Destroyer<Slice::UnitPtr> oldD(oldUnit);
+ if(!FreezeScript::parseSlice(appName, oldUnit, oldSlice, oldCppArgs, debug))
+ {
+ return EXIT_FAILURE;
+ }
+
+ Slice::UnitPtr newUnit = Slice::Unit::createUnit(true, true, ice, underscore);
+ FreezeScript::Destroyer<Slice::UnitPtr> newD(newUnit);
+ if(!FreezeScript::parseSlice(appName, newUnit, newSlice, newCppArgs, debug))
+ {
+ return EXIT_FAILURE;
+ }
+
+ //
+ // Install the evictor types in the Slice units.
+ //
+ FreezeScript::createEvictorSliceTypes(oldUnit);
+ FreezeScript::createEvictorSliceTypes(newUnit);
+
+ //
+ // Read the catalog if necessary.
+ //
+ FreezeScript::CatalogDataMap catalog;
+ if(allDb)
+ {
+ try
+ {
+ catalog = FreezeScript::readCatalog(communicator, dbEnvName);
+ }
+ catch(const FreezeScript::FailureException& ex)
+ {
+ cerr << appName << ": " << ex.reason() << endl;
+ return EXIT_FAILURE;
+ }
+ if(catalog.empty())
+ {
+ cerr << appName << ": no databases in environment `" << dbEnvName << "'" << endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ //
+ // If no input file was provided, then we need to analyze the Slice types.
+ //
+ string descriptors;
+
+ if(inputFile.empty())
+ {
+ ostringstream out;
+ vector<string> missingTypes;
+ vector<string> analyzeErrors;
+
+ FreezeScript::TransformAnalyzer analyzer(oldUnit, newUnit, ignoreTypeChanges, out, missingTypes, analyzeErrors);
+
+ const string evictorKeyName = "::Ice::Identity";
+ const string evictorValueName = "::Freeze::ObjectRecord";
+
+ if(allDb)
+ {
+ //
+ // Add a <database> element for each database in the catalog.
+ //
+ for(FreezeScript::CatalogDataMap::iterator p = catalog.begin(); p != catalog.end(); ++p)
+ {
+ string keyName, valueName;
+
+ if(p->second.evictor)
+ {
+ keyName = evictorKeyName;
+ valueName = evictorValueName;
+ }
+ else
+ {
+ keyName = p->second.key;
+ valueName = p->second.value;
+ }
+
+ //
+ // Look up the key and value types in the old and new Slice definitions.
+ //
+ Slice::TypePtr oldKeyType = findType(oldUnit, keyName);
+ if(!oldKeyType)
+ {
+ cerr << appName << ": type `" << keyName << "' from database `" << p->first
+ << "' not found in old Slice definitions" << endl;
+ }
+ Slice::TypePtr newKeyType = findType(newUnit, keyName);
+ if(!newKeyType)
+ {
+ cerr << appName << ": type `" << keyName << "' from database `" << p->first
+ << "' not found in new Slice definitions" << endl;
+ }
+ Slice::TypePtr oldValueType = findType(oldUnit, valueName);
+ if(!oldValueType)
+ {
+ cerr << appName << ": type `" << valueName << "' from database `" << p->first
+ << "' not found in old Slice definitions" << endl;
+ }
+ Slice::TypePtr newValueType = findType(newUnit, valueName);
+ if(!newValueType)
+ {
+ cerr << appName << ": type `" << valueName << "' from database `" << p->first
+ << "' not found in new Slice definitions" << endl;
+ }
+
+ //
+ // If we are generating an output file, we do not consider a missing type to be
+ // an error. Since the type information comes from the catalog of the old
+ // environment, it's possible that the key or value types are not present in
+ // the new Slice definitions. Rather than abort at this point, we simply emit
+ // a partially-defined <database> element that must be edited by the user.
+ //
+ // If we are not generating an output file, we have to stop now.
+ //
+ if(outputFile.empty() && (!oldKeyType || !newKeyType || !oldValueType || !newValueType))
+ {
+ return EXIT_FAILURE;
+ }
+
+ analyzer.addDatabase(p->first, oldKeyType, newKeyType, oldValueType, newValueType);
+ }
+ }
+ else
+ {
+ string oldKeyName, newKeyName, oldValueName, newValueName;
+
+ if(evictor)
+ {
+ oldKeyName = newKeyName = evictorKeyName;
+ oldValueName = newValueName = evictorValueName;
+ }
+ else
+ {
+ string::size_type pos;
+
+ if(keyTypeNames.empty() || valueTypeNames.empty())
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+
+ pos = keyTypeNames.find(',');
+ if(pos == 0 || pos == keyTypeNames.size())
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+ if(pos == string::npos)
+ {
+ oldKeyName = keyTypeNames;
+ newKeyName = keyTypeNames;
+ }
+ else
+ {
+ oldKeyName = keyTypeNames.substr(0, pos);
+ newKeyName = keyTypeNames.substr(pos + 1);
+ }
+
+ pos = valueTypeNames.find(',');
+ if(pos == 0 || pos == valueTypeNames.size())
+ {
+ usage(appName);
+ return EXIT_FAILURE;
+ }
+ if(pos == string::npos)
+ {
+ oldValueName = valueTypeNames;
+ newValueName = valueTypeNames;
+ }
+ else
+ {
+ oldValueName = valueTypeNames.substr(0, pos);
+ newValueName = valueTypeNames.substr(pos + 1);
+ }
+ }
+
+ //
+ // Look up the key and value types in the old and new Slice definitions.
+ //
+ Slice::TypePtr oldKeyType = findType(oldUnit, oldKeyName);
+ if(!oldKeyType)
+ {
+ cerr << appName << ": type `" << oldKeyName << "' not found in old Slice definitions" << endl;
+ }
+ Slice::TypePtr newKeyType = findType(newUnit, newKeyName);
+ if(!newKeyType)
+ {
+ cerr << appName << ": type `" << newKeyName << "' not found in new Slice definitions" << endl;
+ }
+ Slice::TypePtr oldValueType = findType(oldUnit, oldValueName);
+ if(!oldValueType)
+ {
+ cerr << appName << ": type `" << oldValueName << "' not found in old Slice definitions" << endl;
+ }
+ Slice::TypePtr newValueType = findType(newUnit, newValueName);
+ if(!newValueType)
+ {
+ cerr << appName << ": type `" << newValueName << "' not found in new Slice definitions" << endl;
+ }
+
+ //
+ // Stop now if any of the types could not be found.
+ //
+ if(!oldKeyType || !newKeyType || !oldValueType || !newValueType)
+ {
+ return EXIT_FAILURE;
+ }
+
+ analyzer.addDatabase("", oldKeyType, newKeyType, oldValueType, newValueType);
+ }
+
+ analyzer.finish();
+
+ if(!analyzeErrors.empty())
+ {
+ for(vector<string>::const_iterator p = analyzeErrors.begin(); p != analyzeErrors.end(); ++p)
+ {
+ cerr << appName << ": " << *p << endl;
+ }
+ }
+
+ if(!missingTypes.empty())
+ {
+ sort(missingTypes.begin(), missingTypes.end());
+ unique(missingTypes.begin(), missingTypes.end());
+ if(!analyzeErrors.empty())
+ {
+ cerr << endl;
+ }
+ cerr << "The following types had no matching definitions in the new Slice:" << endl;
+ for(vector<string>::const_iterator p = missingTypes.begin(); p != missingTypes.end(); ++p)
+ {
+ cerr << " " << *p << endl;
+ }
+ }
+
+ if(!analyzeErrors.empty())
+ {
+ return EXIT_FAILURE;
+ }
+
+ descriptors = out.str();
+
+ if(!outputFile.empty())
+ {
+ //
+ // No nativeToUTF8 conversion necessary here, no string converter is installed
+ // by wmain() on Windows and args are assumbed to be UTF8 on Unix platforms.
+ //
+ IceUtilInternal::ofstream of(outputFile);
+ if(!of.good())
+ {
+ cerr << appName << ": unable to open file `" << outputFile << "'" << endl;
+ return EXIT_FAILURE;
+ }
+ of << descriptors;
+ of.close();
+ return EXIT_SUCCESS;
+ }
+ }
+ else
+ {
+ //
+ // Read the input file.
+ //
+ // No nativeToUTF8 conversion necessary here, no string converter is installed
+ // by wmain() on Windows and args are assumbed to be UTF8 on Unix platforms.
+ //
+ IceUtilInternal::ifstream in(inputFile);
+ char buff[1024];
+ while(true)
+ {
+ in.read(buff, 1024);
+ descriptors.append(buff, static_cast<size_t>(in.gcount()));
+ if(in.gcount() < 1024)
+ {
+ break;
+ }
+ }
+ in.close();
+ }
+
+ if(dbEnvName == dbEnvNameNew)
+ {
+ cerr << appName << ": database environment names must be different" << endl;
+ return EXIT_FAILURE;
+ }
+
+ FreezeScript::ObjectFactoryPtr objectFactory = new FreezeScript::ObjectFactory;
+ communicator->addObjectFactory(objectFactory, "");
+
+ //
+ // Transform the database.
+ //
+ DbEnv dbEnv(0);
+ DbEnv dbEnvNew(0);
+ Freeze::TransactionPtr txNew;
+ Freeze::ConnectionPtr connection;
+ Freeze::ConnectionPtr connectionNew;
+ vector<Db*> dbs;
+ int status = EXIT_SUCCESS;
+ try
+ {
+#ifdef _WIN32
+ //
+ // Berkeley DB may use a different C++ runtime.
+ //
+ dbEnv.set_alloc(::malloc, ::realloc, ::free);
+ dbEnvNew.set_alloc(::malloc, ::realloc, ::free);
+#endif
+
+ //
+ // Open the old database environment. Use DB_RECOVER_FATAL if -c is specified.
+ // No transaction is created for the old environment.
+ //
+ // DB_THREAD is for compatibility with Freeze (the catalog)
+ //
+ {
+ u_int32_t flags = DB_THREAD | DB_CREATE | DB_INIT_TXN | DB_INIT_MPOOL;
+ if(catastrophicRecover)
+ {
+ flags |= DB_INIT_LOG | DB_RECOVER_FATAL;
+ }
+ dbEnv.open(dbEnvName.c_str(), flags, FREEZE_SCRIPT_DB_MODE);
+ }
+
+ //
+ // We're creating a connection just to make sure the database environment
+ // isn't locked.
+ //
+ connection = Freeze::createConnection(communicator, dbEnvName, dbEnv);
+
+ //
+ // Open the new database environment and start a transaction.
+ //
+ //
+ // DB_THREAD is for compatibility with Freeze (the catalog)
+ //
+ {
+ u_int32_t flags = DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_CREATE | DB_THREAD;
+ dbEnvNew.open(dbEnvNameNew.c_str(), flags, FREEZE_SCRIPT_DB_MODE);
+ }
+
+ //
+ // TODO: handle properly DbHome config (currently it will break if it's set for the new env)
+ //
+
+ //
+ // Open the catalog of the new environment, and start a transaction.
+ //
+ connectionNew = Freeze::createConnection(communicator, dbEnvNameNew, dbEnvNew);
+ txNew = connectionNew->beginTransaction();
+ DbTxn* txnNew = Freeze::getTxn(txNew);
+
+ if(allDb)
+ {
+ //
+ // Transform all databases in the old catalog.
+ //
+ for(FreezeScript::CatalogDataMap::iterator p = catalog.begin(); p != catalog.end(); ++p)
+ {
+ transformDb(p->second.evictor, communicator, objectFactory, dbEnv, dbEnvNew, p->first, connectionNew,
+ dbs, oldUnit, newUnit, txnNew, purgeObjects, suppress, descriptors);
+ }
+ }
+ else
+ {
+ transformDb(evictor, communicator, objectFactory, dbEnv, dbEnvNew, dbName, connectionNew, dbs,
+ oldUnit, newUnit, txnNew, purgeObjects, suppress, descriptors);
+ }
+ }
+ catch(const DbException& ex)
+ {
+ cerr << appName << ": database error: " << ex.what() << endl;
+ status = EXIT_FAILURE;
+ }
+ catch(const IceUtil::FileLockException&)
+ {
+ cerr << appName << ": error: database environment is locked" << endl;
+ status = EXIT_FAILURE;
+ }
+ catch(...)
+ {
+ try
+ {
+ if(txNew != 0)
+ {
+ txNew->rollback();
+ txNew = 0;
+ }
+ if(connectionNew)
+ {
+ connectionNew->close();
+ connectionNew = 0;
+ }
+ if(connection)
+ {
+ connection->close();
+ connection = 0;
+ }
+ for(vector<Db*>::iterator p = dbs.begin(); p != dbs.end(); ++p)
+ {
+ Db* db = *p;
+ db->close(0);
+ delete db;
+ }
+ try
+ {
+ dbEnv.close(0);
+ }
+ catch(const DbException&)
+ {
+ }
+ try
+ {
+ dbEnvNew.close(0);
+ }
+ catch(const DbException&)
+ {
+ }
+ }
+ catch(const DbException& ex)
+ {
+ cerr << appName << ": database error: " << ex.what() << endl;
+ }
+ throw;
+ }
+
+ if(txNew != 0)
+ {
+ try
+ {
+ if(status == EXIT_FAILURE)
+ {
+ txNew->rollback();
+ }
+ else
+ {
+ txNew->commit();
+
+ //
+ // Checkpoint to migrate changes from the log to the database(s).
+ //
+ dbEnvNew.txn_checkpoint(0, 0, DB_FORCE);
+ }
+
+ for(vector<Db*>::iterator p = dbs.begin(); p != dbs.end(); ++p)
+ {
+ Db* db = *p;
+ db->close(0);
+ delete db;
+ }
+ }
+ catch(const DbException& ex)
+ {
+ cerr << appName << ": database error: " << ex.what() << endl;
+ status = EXIT_FAILURE;
+ }
+ }
+ // Clear the transaction before closing the database environment.
+ txNew = 0;
+
+ if(connectionNew)
+ {
+ connectionNew->close();
+ connectionNew = 0;
+ }
+
+ if(connection)
+ {
+ connection->close();
+ connection = 0;
+ }
+
+ try
+ {
+ dbEnv.close(0);
+ }
+ catch(const DbException&)
+ {
+ }
+
+ try
+ {
+ dbEnvNew.close(0);
+ }
+ catch(const DbException&)
+ {
+ }
+
+ return status;
+}
+
+//COMPILERFIX: Borland C++ 2010 doesn't support wmain for console applications.
+#if defined(_WIN32 ) && !defined(__BCPLUSPLUS__)
+
+int
+wmain(int argc, wchar_t* argv[])
+
+#else
+
+int
+main(int argc, char* argv[])
+#endif
+{
+ Ice::StringSeq args = Ice::argsToStringSeq(argc, argv);
+ assert(args.size() > 0);
+ const string appName = args[0];
+ Ice::CommunicatorPtr communicator;
+ int status = EXIT_SUCCESS;
+ try
+ {
+ communicator = Ice::initialize(args);
+ status = run(args, communicator);
+ }
+ catch(const FreezeScript::FailureException& ex)
+ {
+ string reason = ex.reason();
+ cerr << appName << ": " << reason;
+ if(reason[reason.size() - 1] != '\n')
+ {
+ cerr << endl;
+ }
+ status = EXIT_FAILURE;
+ }
+ catch(const std::exception& ex)
+ {
+ cerr << appName << ": " << ex.what() << endl;
+ status = EXIT_FAILURE;
+ }
+ catch(...)
+ {
+ cerr << appName << ": unknown exception" << endl;
+ status = EXIT_FAILURE;
+ }
+
+ if(communicator)
+ {
+ communicator->destroy();
+ }
+
+ return status;
+}
+