diff --git a/src/updater.cpp b/src/updater.cpp --- a/src/updater.cpp +++ b/src/updater.cpp @@ -1,875 +1,836 @@ #include "updater.h" #include "psettings.h" #include #include #include #include #include #include #include DatabaseUpdater::DatabaseUpdater() : QObject() , _pset(0) , _revisionBefore(0) , _revisionAfter(0) { } DatabaseUpdater::~DatabaseUpdater() { delete _pset; } int DatabaseUpdater::run(ProgramSettings& a_pset) { _pset = new ProgramSettings(a_pset); if (!checkArguments()) return -1; if (!readConfig()) return -1; if (!loadScriptsFromResource()) return -1; if (!runScripts()) return -1; return 0; } //--------------------------------------------------------------------------- bool DatabaseUpdater::checkArguments() { if (_pset->controlFile.isEmpty()) { emit error(QStringLiteral("Не задан файл конфигурации.")); return false; } //если не задано полное имя файла конфигурации, то искать его в текущем каталоге QString filename = _pset->controlFile; if (!QDir::isAbsolutePath(filename)) filename = QStringLiteral("%1/%2").arg(QDir::currentPath()).arg(_pset->controlFile); //проверить наличие файла if (!QFile::exists(filename)) { emit error(QStringLiteral("Не найден файл конфигурации\n") + QDir::toNativeSeparators(filename)); return false; } _pset->controlFile = filename; return true; } //--------------------------------------------------------------------------- bool DatabaseUpdater::readConfig() { //открываем входной файл QFile controlFile(_pset->controlFile); if (!controlFile.open(QIODevice::ReadOnly)) { emit error(QStringLiteral("Сбой при загрузке файла конфигурации:\n" "Невозможно открыть для чтения файл %1") .arg(QDir::toNativeSeparators(_pset->controlFile))); return false; } //парсим входной файл QXmlStreamReader xml; xml.setDevice(&controlFile); emit message(QStringLiteral("Загрузка конфигурации из файла\n%1").arg(QDir::toNativeSeparators(_pset->controlFile))); //идём по элементам документа while (!xml.atEnd()) { xml.readNext(); if (xml.isStartElement() && xml.name() == "package") if (!readPackage(xml)) return false; } if (xml.hasError()) { emit error(QStringLiteral("Сбой при загрузке файла конфигурации:\n" "Ошибка: %1, строка %2, позиция %3") .arg(xml.errorString()) .arg(xml.lineNumber()) .arg(xml.columnNumber())); return false; } controlFile.close(); //найти файл с расширением pkginfo и именем как у входного файла, если такого нет, //то использовать первый попавшийся файл с расширением pkginfo; //прочитать из него идентификатор пакета и имя базы данных; //если файла нет, то и не надо QFileInfo fi(controlFile); QStringList files = QDir(QFileInfo(controlFile).path(), "*.pkginfo", QDir::NoSort, QDir::Files).entryList(); if (!files.isEmpty()) { QString pkgfile = QFileInfo(controlFile).completeBaseName() + ".pkginfo"; if (!files.contains(pkgfile, Qt::CaseInsensitive)) pkgfile = files.at(0); pkgfile = QFileInfo(controlFile).path() + '/' + pkgfile; emit message(QStringLiteral("Загрузка конфигурации из файла\n%1").arg(QDir::toNativeSeparators(pkgfile))); KSettings pkginfo(pkgfile); pkginfo.beginGroup("package"); QString s = pkginfo.value("dbname").toString(); if (!s.isEmpty() && _pset->database.isEmpty()) _pset->database = s; QStringList ver = pkginfo.value("version").toString().split(QChar('.')); if (ver.size() > 1) { s = ver.at(1); bool ok; int i = s.toInt(&ok); if (ok) _pset->dbVersion = i; } _pset->packageId = pkginfo.value("id").toString(); } //после загрузки конфигурации из файлов должно быть определено имя БД if (_pset->database.isEmpty()) { emit error(QStringLiteral("Не задано имя базы данных")); return false; } return true; } //--------------------------------------------------------------------------- bool DatabaseUpdater::readPackage(QXmlStreamReader& a_xml) { const QXmlStreamAttributes& packageAttrs = a_xml.attributes(); QString packageId = packageAttrs.value("id").toString(); QString uriAttr = packageAttrs.value("uri").toString(); bool uri = !((uriAttr == "no") || (uriAttr == "нет")); Package* package = new Package(packageId, uri); _packages.append(package); //собираем информацию о скриптах данного пакета const QString SCRIPT_NODE("script"); const QString REVISION_ATTR("revision"); const QString TRANSACTION_ATTR("transaction"); const QString COMMENT_ATTR("comment"); const QString SCRIPT_ATTR("file"); - const QString URI_DATA_ATTR("udata"); const QString NO_REQUIRED_ATTR = QStringLiteral("Неправильное содержание файла конфигурации:\n" "Не указан обязательный атрибут «%1» пакета «%2»"); while (!a_xml.atEnd()) { a_xml.readNext(); if (a_xml.isStartElement() && a_xml.name() == SCRIPT_NODE) { const QXmlStreamAttributes& attrs = a_xml.attributes(); //проверка наличия обязательных атрибутов if (!attrs.hasAttribute(REVISION_ATTR)) { emit error(NO_REQUIRED_ATTR.arg(REVISION_ATTR).arg(packageId)); return false; } if (!attrs.hasAttribute(SCRIPT_ATTR)) { emit error(NO_REQUIRED_ATTR.arg(SCRIPT_ATTR).arg(packageId)); return false; } //проверка правильности указания атрибутов bool ok; int revision = attrs.value(REVISION_ATTR).toString().toInt(&ok); if (!ok) { emit error(QStringLiteral( "Атрибут «revision» пакета «%1» должен быть числом") .arg(packageId)); return false; } //заносим информацию о скрипте в список скриптов пакета QString file = attrs.value(SCRIPT_ATTR).toString(); - QString uriScript = attrs.value(URI_DATA_ATTR).toString(); QString comment = attrs.value(COMMENT_ATTR).toString(); QString strans = attrs.value(TRANSACTION_ATTR).toString(); bool transaction = !((strans == "0") || (strans.compare("no", Qt::CaseInsensitive) == 0) || (strans.compare("нет", Qt::CaseInsensitive) == 0)); DatabaseScript* script = new DatabaseScript(revision, transaction, - file, uriScript, comment); + file, comment); package->addScript(script); } } //упорядочить скрипты по возрастанию номеров ревизий package->sortScripts(); return true; } //--------------------------------------------------------------------------- bool DatabaseUpdater::runScripts() { //определение пакета, скрипты которого будут выполняться auto pp = std::find_if(_packages.cbegin(), _packages.cend(), [this](Package* p){return p->id() == _pset->packageId;}); if (pp == _packages.cend()) { emit error(QStringLiteral("Для пакета «%1» не задано ни одного " "сценария создания базы данных") .arg(_pset->packageId)); return false; } //проверка наличия файлов со скриптами на диске Package* package = *pp; QStringList paths; QFileInfo fi(_pset->controlFile); paths << fi.canonicalPath() << fi.canonicalPath() + "/script" << QDir::currentPath(); DatabaseScript* script = 0; auto packageScripts = package->scripts(); auto scriptsEnd = packageScripts.cend(); for (auto it = packageScripts.cbegin(); it != scriptsEnd; ++it) { script = *it; if (!script->findScript(paths)) { emit error(QStringLiteral("Не найден файл сценария создания " "базы данных %1") .arg(script->script())); return false; } - if (package->uri() && !script->findUriScript(paths)) - { - emit error(QStringLiteral("Не найден файл условно-реальной " - "информации %1") - .arg(script->uriScript())); - return false; - } } QString curDate = QDate::currentDate().toString(Qt::ISODate); SqlProcessor proc; SqlProcessor uriProc; emit message(QStringLiteral("Подключение к серверу баз данных")); //пока идут служебные подключения к БД и запросы, подключаем только сигнал error connect(&proc, SIGNAL(error(const QString&, const QString&, const QString&)), this, SIGNAL(sqlError(const QString&, const QString&, const QString&))); connect(&uriProc, SIGNAL(error(const QString&, const QString&, const QString&)), this, SIGNAL(sqlError(const QString&, const QString&, const QString&))); //---- template1 ---- //попытка подключения к template1 и получение списка таблиц if (!proc.connectdb(_pset->host, _pset->port, "template1", _pset->username, _pset->password)) return false; QStringList template1Tables = databaseTableList(proc); int currentDatabaseRevision = 0; //БД нет или пустая //удаление БД если задано параметром при запуске программы bool dbExists = databaseExists(proc, _pset->database); if (dbExists && _pset->dropdb) { emit message(QStringLiteral("Удаление базы данных %1").arg(_pset->database)); if (!dropDatabase(proc, _pset)) return false; dbExists = false; } //создание БД если она не существует if (!dbExists) { emit message(QStringLiteral("Создание базы данных %1").arg(_pset->database)); if (!createDatabase(proc, _pset)) return false; } QString uriDatabase = _pset->database + "_u"; if (package->uri()) { //удаление учебной БД если задано параметром при запуске программы bool uriDbExists = databaseExists(proc, uriDatabase); if (uriDbExists && _pset->dropdb) { emit message(QStringLiteral("Удаление учебной базы данных %1").arg(uriDatabase)); if (!dropDatabase(proc, _pset, uriDatabase)) return false; uriDbExists = false; } //создание учебной БД если она не существует if (!uriDbExists) { emit message(QStringLiteral("Создание учебной базы данных %1").arg(uriDatabase)); if (!createDatabase(proc, _pset, uriDatabase)) return false; } } connect(&proc, SIGNAL(afterConnect(QString, QString, QString, QString, QString)), this, SLOT(afterConnect(QString, QString, QString, QString, QString))); //---- database ---- //подключение к созданной/существующей БД и получение списка таблиц if (!proc.connectdb(_pset->host, _pset->port, _pset->database, _pset->username, _pset->password)) return false; QStringList databaseTables = databaseTableList(proc); //подключение к учебной БД и получение списка таблиц QStringList uriDatabaseTables; if (package->uri()) { //---- database_u ---- if (!uriProc.connectdb(_pset->host, _pset->port, uriDatabase, _pset->username, _pset->password)) return false; uriDatabaseTables = databaseTableList(uriProc); } //считывание данных о версии БД из таблицы dm_version, //эта таблица существует только в основной БД, версия учебной должна совпадать bool legacyDatabase = false; proc.execSQL("select relnatts from pg_class " "where relname='dm_version' and relkind='r'"); PGresult* pgresult = proc.result().pgresult; //если пустой результат запроса, значит таблицы dm_version нет в БД // bool createDmVersion = data.size() == 0; bool createDmVersion = PQntuples(pgresult) == 0; //если таблица есть, то сравнить число её атрибутов с эталоном if (!createDmVersion) { const char* pgvalue = PQgetvalue(pgresult, 0, 0); if (QByteArray::fromRawData(pgvalue, qstrlen(pgvalue)).toInt() != DM_VERSION_FIELDS_COUNT) { //не та структура, удаляем таблицу if (!proc.execSQL("drop table dm_version")) return false; createDmVersion = true; } } if (createDmVersion) { //БД уже существовала, а таблицы dm_version в ней нет, или не та структура if (databaseTables != template1Tables) { legacyDatabase = true; currentDatabaseRevision = 1; } emit message(QStringLiteral("Создание таблицы изменений БД")); proc.execSQL("begin"); if (!proc.execute(_createDmVersionScript)) { emit error(QStringLiteral("Сбой при создании таблицы изменений БД")); return false; } if (legacyDatabase) { //запись информации о версии №1 в unversioned БД if (!proc.execSQL(QStringLiteral("insert into dm_version " "(revision,package_id,comment,gen_date) values " "(1,'%1','Изначальная версия','%2')") .arg(package->id(), curDate))) return false; } proc.execSQL("commit"); } else { proc.execSQL(QStringLiteral("select max(revision), count(*) from dm_version " "where package_id='%1'") .arg(package->id())); PGresult* pgresult = proc.result().pgresult; if (PQntuples(pgresult)) { const char* pgvalue = PQgetvalue(pgresult, 0, 1); if (QByteArray::fromRawData(pgvalue, qstrlen(pgvalue)).toInt() > 0) { pgvalue = PQgetvalue(pgresult, 0, 0); currentDatabaseRevision = QByteArray::fromRawData(pgvalue, qstrlen(pgvalue)).toInt(); } } } //создание языка plpgsql if (!createLanguagePlpgsql(proc, uriProc, package->uri())) return false; _revisionAfter = _revisionBefore = currentDatabaseRevision; //выполнение необходимых скриптов connect(&proc, SIGNAL(progress(int)), this, SIGNAL(progress(int))); connect(&uriProc, SIGNAL(progress(int)), this, SIGNAL(progress(int))); scriptsEnd = packageScripts.cend(); for (auto it = packageScripts.cbegin(); it != scriptsEnd; ++it) { script = *it; int revision = script->revision(); bool transaction = script->transaction(); //если была найдена изначальная БД, то всё уже сделано для 1-й версии if ((revision == 1) && legacyDatabase) continue; //пропускаем уже установленные версии БД if (revision <= currentDatabaseRevision) continue; if (revision == 1) emit message(QStringLiteral("Создание базы данных версии %1").arg(revision)); else emit message(QStringLiteral("Обновление базы данных до версии %1").arg(revision)); //подготовка скрипта к выполнению QList mainScript; QList globalsScript; prepareScripts(script->script(), mainScript, globalsScript); //если в скрипте были команды создания глобальных объектов, то выполнить //их в отдельной транзакции if (globalsScript.size() > 0) { proc.execSQL("begin"); if (!proc.execute(globalsScript)) return false; proc.execSQL("commit"); } //выполнение скрипта if (transaction) proc.execSQL("begin"); if (!proc.execute(mainScript)) return false; //выполнение скриптов в учебной БД, если задано её создание if (package->uri()) { if (transaction) uriProc.execSQL("begin"); if (revision == 1) emit message(QStringLiteral("Создание учебной базы данных версии %1").arg(revision)); else emit message(QStringLiteral("Обновление учебной базы данных до версии %1").arg(revision)); if (!uriProc.execute(mainScript)) { if (transaction) proc.execSQL("rollback"); return false; } - if (!script->uriScript().isEmpty()) - { - emit message(QStringLiteral("Загрузка условной информации в учебную базу данных")); - - if (!uriProc.execute(script->uriScript())) - { - if (transaction) - proc.execSQL("rollback"); - return false; - } - } if (!uriProc.execute(_setSearchPathScript)) return false; clearMaclabels(uriProc); if (transaction) uriProc.execSQL("commit"); } //сброс параметра search_path, он мог быть изменён при выполнении скрипта if (!proc.execute(_setSearchPathScript)) return false; //запись информации о версии в БД if (!proc.execSQL(QStringLiteral("insert into dm_version " "(revision,package_id,comment,gen_date) values " "(%1,'%2','%3','%4')") .arg(revision) .arg(package->id()) .arg(script->comment()) .arg(curDate))) return false; //сброс мандатных меток, если они есть в БД clearMaclabels(proc); if (transaction) proc.execSQL("commit"); _revisionAfter = revision; } return true; } //--------------------------------------------------------------------------- bool DatabaseUpdater::databaseExists(SqlProcessor& a_proc, const QString& a_dbname) { a_proc.execSQL(QStringLiteral("select 1 from pg_database where datname='%1\'").arg(a_dbname)); return PQntuples(a_proc.result().pgresult) == 1; } //--------------------------------------------------------------------------- bool DatabaseUpdater::createDatabase(SqlProcessor& a_proc, ProgramSettings* a_pset, const QString& a_dbname) { if (a_dbname.isEmpty() && a_pset->database.isEmpty()) return false; QString dbname = (a_dbname.isEmpty() ? a_pset->database : a_dbname); //если уже подключены к указанной БД, то отключение if (a_proc.database() == dbname) a_proc.disconnectdb(); //если не подключены, то подключение к template1 if (a_proc.database().isNull()) { if (!a_proc.connectdb(a_pset->host, a_pset->port, "template1", a_pset->username, a_pset->password)) return false; } //если БД уже существует, то создавать не надо if (databaseExists(a_proc, dbname)) return true; return a_proc.execSQL(QString("create database \"%1\"").arg(dbname)) && a_proc.commandStatus() == QString("CREATE DATABASE"); } //--------------------------------------------------------------------------- bool DatabaseUpdater::dropDatabase(SqlProcessor& a_proc, ProgramSettings* a_pset, const QString& a_dbname) { if (a_pset->database.isEmpty()) return false; QString dbname = (a_dbname.isEmpty() ? a_pset->database : a_dbname); //если уже подключены к указанной БД, то отключение if (a_proc.database() == dbname) a_proc.disconnectdb(); //если не подключены, то подключение к template1 if (a_proc.database().isNull()) { if (!a_proc.connectdb(a_pset->host, a_pset->port, "template1", a_pset->username, a_pset->password)) return false; } //если БД не существует, то удалять не надо if (!databaseExists(a_proc, dbname)) return true; return a_proc.execSQL(QString("drop database \"%1\"").arg(dbname)) && a_proc.commandStatus() == QString("DROP DATABASE"); } //--------------------------------------------------------------------------- // Получение списка таблиц в БД //--------------------------------------------------------------------------- QStringList DatabaseUpdater::databaseTableList(SqlProcessor& a_proc) { QStringList result; if (!a_proc.execSQL("select tablename from pg_tables order by tablename")) return result; SqlResult sqlresult = a_proc.result(); PGresult* pgresult = sqlresult.pgresult; QTextCodec* codec = sqlresult.codec; int numTuples = PQntuples(pgresult); for (int i = 0; i < numTuples; ++i) { if (PQgetisnull(pgresult, i, 0)) result.append(QString()); else result.append(codec->toUnicode(PQgetvalue(pgresult, i, 0))); } return result; } //--------------------------------------------------------------------------- bool DatabaseUpdater::loadScriptsFromResource() { QString errmess = QStringLiteral("Сбой при чтении ресурсов программы"); SqlProcessor proc; QFile resFile(":/dm_version.sql"); if (!(resFile.open(QIODevice::ReadOnly))) { emit error(errmess); return false; } _createDmVersionScript = proc.parse(resFile); resFile.close(); resFile.setFileName(":/create_helper_functions.sql"); if (!(resFile.open(QIODevice::ReadOnly))) { emit error(errmess); return false; } _createHelperFunctionsScript = proc.parse(resFile); resFile.close(); resFile.setFileName(":/drop_helper_functions.sql"); if (!(resFile.open(QIODevice::ReadOnly))) { emit error(errmess); return false; } _dropHelperFunctionsScript = proc.parse(resFile); resFile.close(); resFile.setFileName(":/set_search_path.sql"); if (!(resFile.open(QIODevice::ReadOnly))) { emit error(errmess); return false; } _setSearchPathScript = proc.parse(resFile); return true; } //--------------------------------------------------------------------------- // Подготавливает скрипт, считанный из файла, к выполнению в БД. // Команды анализируются и разделяются на два скрипта — основной и глобальных // объектов. Обработка заключается в следующем: // 1. Команды создания глобальных объектов // CREATE GROUP // CREATE USER // CREATE ROLE // CREATE TABLESPACE // заменяются вызовами функций-обёрток для их безопасного выполнения. // Эти вызовы вставляются в скрипт глобальных объектов. В этот же скрипт // добавляются команды создания функций-обёрток и их удаления. // 2. Команды установки кодировки клиента // SET CLIENT_ENCODING // SET NAMES // \encoding // добавляются в оба скрипта. // 3. Команда подключения языка plpgsql // CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE plpgsql // игнорируется и не попадает ни в один скрипт. Создание языка plpgsql // производится отдельно, перед началом выполнения скриптов. //--------------------------------------------------------------------------- void DatabaseUpdater::prepareScripts(const QString& a_filename, QList& oa_mainScript, QList& oa_globalsScript) { //пропустим файл через парсер, на выходе список команд bool helperInjected = false; SqlProcessor proc; QList result = proc.parse(a_filename); //обход списка и анализ команд auto end = result.cend(); for (auto it = result.cbegin(); it != end; ++it) { const QByteArray& line = *it; int i = 0; int end = line.size(); //пропускаем первое слово, перед ним пробелов нет, парсер их убирает while (i != end && !QChar(line.at(i)).isSpace()) ++i; //анализируем первое слово QByteArray command1 = line.left(i).toLower(); if (command1 == "\\encoding") { oa_mainScript.append(line); oa_globalsScript.append(line); continue; } //переход на начало второго слова while (i != end && QChar(line.at(i)).isSpace()) ++i; int p = i; //пропускаем второе слово while (i != end && !QChar(line.at(i)).isSpace()) ++i; //анализируем команду QByteArray command2 = line.mid(p, i - p).toLower(); if (command1 == "set") { oa_mainScript.append(line); if (command2 == "names" || command2.startsWith("client_encoding")) oa_globalsScript.append(line); } else if (command1 == "create") { if (command2 == "group" || command2 == "user" || command2 == "role" || command2 == "tablespace") { //добавляем в выходной скрипт globals вызов безопасной функции-обёртки if (!helperInjected) { //но сначала вставляем команды создания функций-обёрток helperInjected = true; for (const auto& helperScript: _createHelperFunctionsScript) oa_globalsScript.append(helperScript); } //переход на начало имени объекта while (i != end && QChar(line.at(i)).isSpace()) ++i; QByteArray name; if (i != end) { if (line.at(i) == '"') { //если имя объекта начинается с двойной кавычки, то пропуск до следующей //двойной кавычки ++i; //пропустить открывающую двойную кавычку p = i; while (i != end && line.at(i) != '"') ++i; name = line.mid(p, i - p); if (i != end) ++i; //пропустить закрывающую двойную кавычку } else { //пропуск до конца слова p = i; while (i != end && !QChar(line.at(i)).isSpace()) ++i; name = line.mid(p, i - p); if (name.size() > 0 && name.at(name.size() - 1) == ';') name.chop(1); } } QByteArray parameters(line.right(end - i)); if (parameters.size() > 0 && parameters.at(parameters.size() - 1) == ';') parameters.chop(1); QByteArray safeCall("select _rct_safe_create_"); safeCall.append(command2).append("('").append(name).append("','").append(parameters).append("');"); oa_globalsScript.append(safeCall); } else if (command2 == "trusted" || command2 == "procedural" || command2 == "language") { //create language не нужен continue; } else { //create неглобального объекта, в основной скрипт oa_mainScript.append(line); } } else { //все остальные команды в основной скрипт oa_mainScript.append(line); } } //цикл по командам if (helperInjected) oa_globalsScript.append(_dropHelperFunctionsScript); } //--------------------------------------------------------------------------- void DatabaseUpdater::clearMaclabels(SqlProcessor& a_proc) { a_proc.execSQL("select distinct 1 from pg_attribute a join pg_class c " "on (a.attrelid=c.oid) where c.relname='pg_class' and a.attname='relmaclabel'"); if (PQntuples(a_proc.result().pgresult) == 1) a_proc.execSQL("update pg_class set relmaclabel=null"); } //--------------------------------------------------------------------------- bool DatabaseUpdater::createLanguagePlpgsql(SqlProcessor& a_proc, SqlProcessor& a_uriProc, bool a_uri) { const QString CREATE_SQL = QStringLiteral("create language plpgsql"); const QString CHECK_SQL = QStringLiteral("select 1 from pg_language where lanname='plpgsql'"); a_proc.execSQL(CHECK_SQL); if (PQntuples(a_proc.result().pgresult) == 0) { if (!a_proc.execSQL(CREATE_SQL)) return false; } if (a_uri) { a_uriProc.execSQL(CHECK_SQL); if (PQntuples(a_uriProc.result().pgresult) == 0) { if (!a_uriProc.execSQL(CREATE_SQL)) return false; } } return true; } //--------------------------------------------------------------------------- void DatabaseUpdater::afterConnect(const QString& a_host, const QString& a_port, const QString& a_database, const QString& a_username, const QString& a_password) { Q_UNUSED(a_port); Q_UNUSED(a_password); emit logConnectionParameters(a_host, a_database, a_username); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool DatabaseScript::findScript(const QStringList& a_paths) { _script.replace(QChar('\\'), QChar('/')); foreach (QString path, a_paths) { QDir dir(path); if (dir.exists(_script)) { _script = dir.absoluteFilePath(_script); return true; } } return false; } - -//--------------------------------------------------------------------------- -bool DatabaseScript::findUriScript(const QStringList& a_paths) -{ - _uriScript.replace(QChar('\\'), QChar('/')); - if (_uriScript.isEmpty()) - return true; - - foreach (QString path, a_paths) - { - QDir dir(path); - if (dir.exists(_uriScript)) - { - _uriScript = dir.absoluteFilePath(_uriScript); - return true; - } - } - return false; -} diff --git a/src/updater.h b/src/updater.h --- a/src/updater.h +++ b/src/updater.h @@ -1,127 +1,123 @@ #ifndef UPDATER_H #define UPDATER_H #include #include class DatabaseScript { friend class DatabaseUpdater; public: DatabaseScript(int a_revision, bool a_transaction, const QString& a_script, - const QString& a_uriScript, const QString& a_comment) + const QString& a_comment) : _revision(a_revision) , _transaction(a_transaction) , _script(a_script) - , _uriScript(a_uriScript) , _comment(a_comment) { } int revision() const { return _revision; } bool transaction() const { return _transaction; } QString script() const { return _script; } - QString uriScript() const { return _uriScript; } QString comment() const { return _comment; } bool findScript(const QStringList& a_paths); - bool findUriScript(const QStringList& a_paths); private: int _revision; bool _transaction; QString _script; - QString _uriScript; QString _comment; }; class Package { public: Package(const QString& a_id, bool a_uri = true) : _id(a_id) , _uri(a_uri) { } void addScript(DatabaseScript* a_script) { _scripts.append(a_script); } void sortScripts() { qSort(_scripts.begin(), _scripts.end(), scriptRevisionLessThan); } static bool scriptRevisionLessThan(DatabaseScript* a_script1, DatabaseScript* a_script2) { return a_script1->revision() < a_script2->revision(); } bool uri() const { return _uri; } QString id() const { return _id; } QList scripts() const { return _scripts; } private: QList _scripts; QString _id; bool _uri; }; class SqlProcessor; class ProgramSettings; class QByteArray; class QByteArrayMatcher; class QXmlStreamReader; class DatabaseUpdater : public QObject { Q_OBJECT public: DatabaseUpdater(); ~DatabaseUpdater() override; int run(ProgramSettings& a_pset); int revisionBefore() const { return _revisionBefore; } int revisionAfter() const { return _revisionAfter; } signals: void error(const QString& a_message); void sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command); void logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username); void progress(int); void message(const QString& a_message); public slots: void afterConnect(const QString& a_host, const QString& a_port, const QString& a_database, const QString& a_username, const QString& a_password); private: enum { DM_VERSION_FIELDS_COUNT = 5 }; ProgramSettings* _pset; QString _logText; QList _packages; QList _createDmVersionScript; QList _createHelperFunctionsScript; QList _dropHelperFunctionsScript; QList _setSearchPathScript; int _revisionBefore; int _revisionAfter; QStringList databaseTableList(SqlProcessor& a_proc); bool checkArguments(); bool readConfig(); bool readPackage(QXmlStreamReader& a_xml); bool loadScriptsFromResource(); bool runScripts(); bool databaseExists(SqlProcessor& a_proc, const QString& a_dbname); bool createDatabase(SqlProcessor& a_proc, ProgramSettings* a_pset, const QString& a_dbname = QString()); bool dropDatabase(SqlProcessor& a_proc, ProgramSettings* a_pset, const QString& a_dbname = QString()); bool createLanguagePlpgsql(SqlProcessor& a_proc, SqlProcessor& a_uriProc, bool a_uri); void prepareScripts(const QString& a_filename, QList& oa_mainScript, QList& oa_globalsScript); void clearMaclabels(SqlProcessor& a_proc); }; //!главный класс приложения #endif //UPDATER_H