diff --git a/resource/create_helper_functions.sql b/resource/create_helper_functions.sql --- a/resource/create_helper_functions.sql +++ b/resource/create_helper_functions.sql @@ -1,59 +1,59 @@ -CREATE FUNCTION safe_create_group(text,text) RETURNS integer +CREATE FUNCTION _rct_safe_create_group(text,text) RETURNS integer AS ' DECLARE a_groupname ALIAS FOR $1; a_parameters ALIAS FOR $2; BEGIN perform 1 from pg_group where groname=a_groupname; if not found then - execute ''create group "''||a_groupname||''" ''||a_parameters; + execute ''create group ''||quote_ident(a_groupname)||'' ''||a_parameters; end if; return 0; END; ' LANGUAGE plpgsql; -CREATE FUNCTION safe_create_user(text,text) RETURNS integer +CREATE FUNCTION _rct_safe_create_user(text,text) RETURNS integer AS ' DECLARE a_username ALIAS FOR $1; a_parameters ALIAS FOR $2; BEGIN perform 1 from pg_user where usename=a_username; if not found then - execute ''create user "''||a_username||''" ''||a_parameters; + execute ''create user ''||quote_ident(a_username)||'' ''||a_parameters; end if; return 0; END; ' LANGUAGE plpgsql; -CREATE FUNCTION safe_create_role(text,text) RETURNS integer +CREATE FUNCTION _rct_safe_create_role(text,text) RETURNS integer AS ' DECLARE a_rolename ALIAS FOR $1; a_parameters ALIAS FOR $2; BEGIN perform 1 from pg_roles where rolname=a_rolename; if not found then - execute ''create role "''||a_rolename||''" ''||a_parameters; + execute ''create role ''||quote_ident(a_rolename)||'' ''||a_parameters; end if; return 0; END; ' LANGUAGE plpgsql; -CREATE FUNCTION safe_create_tablespace(text,text) RETURNS integer +CREATE FUNCTION _rct_safe_create_tablespace(text,text) RETURNS integer AS ' DECLARE a_tablespacename ALIAS FOR $1; a_parameters ALIAS FOR $2; BEGIN perform 1 from pg_tablespace where spcname=a_tablespacename; if not found then - execute ''create tablespace "''||a_tablespacename||''" ''||a_parameters; + execute ''create tablespace ''||quote_ident(a_tablespacename)||'' ''||a_parameters; end if; return 0; END; ' LANGUAGE plpgsql; diff --git a/resource/drop_helper_functions.sql b/resource/drop_helper_functions.sql --- a/resource/drop_helper_functions.sql +++ b/resource/drop_helper_functions.sql @@ -1,4 +1,4 @@ -DROP FUNCTION safe_create_group(text,text); -DROP FUNCTION safe_create_user(text,text); -DROP FUNCTION safe_create_role(text,text); -DROP FUNCTION safe_create_tablespace(text,text); +DROP FUNCTION _rct_safe_create_group(text,text); +DROP FUNCTION _rct_safe_create_user(text,text); +DROP FUNCTION _rct_safe_create_role(text,text); +DROP FUNCTION _rct_safe_create_tablespace(text,text); diff --git a/src/updater.cpp b/src/updater.cpp --- a/src/updater.cpp +++ b/src/updater.cpp @@ -1,888 +1,888 @@ #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( QString::fromUtf8( "Не задан файл конфигурации." ) ); return false; } //если не задано полное имя файла конфигурации, то искать его в текущем каталоге QString filename = _pset->controlFile; if ( !QDir::isAbsolutePath( filename ) ) filename = QString( "%1/%2" ).arg( QDir::currentPath() ).arg( _pset->controlFile ); //проверить наличие файла if ( !QFile::exists( filename ) ) { emit error( QString::fromUtf8( "Не найден файл конфигурации\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( QString::fromUtf8( "Сбой при загрузке файла конфигурации:\n" "Невозможно открыть для чтения файл %1" ).arg( QDir::toNativeSeparators( _pset->controlFile ) ) ); return false; } //парсим входной файл QXmlStreamReader xml; xml.setDevice( &controlFile ); emit message( QString::fromUtf8( "Загрузка конфигурации из файла\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( QString::fromUtf8( "Сбой при загрузке файла конфигурации:\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( QString::fromUtf8( "Загрузка конфигурации из файла\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( QString::fromUtf8( "Не задано имя базы данных" ) ); 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 = QString::fromUtf8( "Неправильное содержание файла конфигурации:\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( QString::fromUtf8( "Атрибут «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 ); package->addScript( script ); } } //упорядочить скрипты по возрастанию номеров ревизий package->sortScripts(); return true; } //--------------------------------------------------------------------------- bool DatabaseUpdater::runScripts() { //определение пакета, скрипты которого будут выполняться Package* package = 0; QListIterator it( _packages ); while ( it.hasNext() ) { package = it.next(); if ( package->id() == _pset->packageId ) break; else package = 0; } if ( !package ) { emit error( QString::fromUtf8( "Для пакета «%1» не задано ни одного " "сценария создания базы данных" ).arg( _pset->packageId ) ); return false; } //проверка наличия файлов со скриптами на диске QStringList paths; QFileInfo fi( _pset->controlFile ); paths << fi.canonicalPath() << fi.canonicalPath() + "/script" << QDir::currentPath(); DatabaseScript* script = 0; QListIterator it2( package->scripts() ); while ( it2.hasNext() ) { script = it2.next(); if ( !script->findScript( paths ) ) { emit error( QString::fromUtf8( "Не найден файл сценария создания " "базы данных %1" ).arg( script->script() ) ); return false; } if ( package->uri() && !script->findUriScript( paths ) ) { emit error( QString::fromUtf8( "Не найден файл условно-реальной " "информации %1" ).arg( script->uriScript() ) ); return false; } } QString curDate = QDate::currentDate().toString( Qt::ISODate ); SqlProcessor proc; SqlProcessor uriProc; emit message( QString::fromUtf8( "Подключение к серверу баз данных" ) ); //пока идут служебные подключения к БД и запросы, подключаем только сигнал 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( QString::fromUtf8( "Удаление базы данных %1" ).arg( _pset->database ) ); if ( !dropDatabase( proc, _pset ) ) return false; dbExists = false; } //создание БД если она не существует if ( !dbExists ) { emit message( QString::fromUtf8( "Создание базы данных %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( QString::fromUtf8( "Удаление учебной базы данных %1" ).arg( uriDatabase ) ); if ( !dropDatabase( proc, _pset, uriDatabase ) ) return false; uriDbExists = false; } //создание учебной БД если она не существует if ( !uriDbExists ) { emit message( QString::fromUtf8( "Создание учебной базы данных %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( QString::fromUtf8( "Создание таблицы изменений БД" ) ); proc.execSQL( "begin" ); if ( !proc.execute( _createDmVersionScript ) ) { emit error( QString::fromUtf8( "Сбой при создании таблицы изменений БД" ) ); return false; } if ( legacyDatabase ) { //запись информации о версии №1 в unversioned БД if ( !proc.execSQL( QString::fromUtf8( "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( QString("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)) ); QListIterator iter( package->scripts() ); while ( iter.hasNext() ) { script = iter.next(); int revision = script->revision(); bool transaction = script->transaction(); //если была найдена изначальная БД, то всё уже сделано для 1-й версии if ( (revision == 1) && legacyDatabase ) continue; //пропускаем уже установленные версии БД if ( revision <= currentDatabaseRevision ) continue; if ( revision == 1 ) emit message( QString::fromUtf8( "Создание базы данных версии %1" ).arg( revision ) ); else emit message( QString::fromUtf8( "Обновление базы данных до версии %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( QString::fromUtf8( "Создание учебной базы данных версии %1" ).arg( revision ) ); else emit message( QString::fromUtf8( "Обновление учебной базы данных до версии %1" ).arg( revision ) ); if ( !uriProc.execute( mainScript ) ) { if ( transaction ) proc.execSQL( "rollback" ); return false; } if ( !script->uriScript().isEmpty() ) { emit message( QString::fromUtf8( "Загрузка условной информации в учебную базу данных" ) ); 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( QString::fromUtf8( "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( QString( "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, QString( "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, QString( "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 = QString::fromUtf8( "Сбой при чтении ресурсов программы" ); 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 ); //обход списка и анализ команд QListIterator it( result ); while ( it.hasNext() ) { const QByteArray originalLine = it.next(); QByteArray line = originalLine.toLower(); int i = 0; int end = line.size(); //пропускаем первое слово, перед ним пробелов нет, парсер их убирает while ( i != end && !QChar(line.at( i )).isSpace() ) ++i; //анализируем первое слово QByteArray command1 = line.left( i ); if ( command1 == "\\encoding" ) { oa_mainScript.append( originalLine ); oa_globalsScript.append( originalLine ); 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 ); if ( command1 == "set" ) { oa_mainScript.append( originalLine ); if ( command2 == "names" || command2 == "client_encoding" ) oa_globalsScript.append( originalLine ); } else if ( command1 == "create" ) { if ( command2 == "group" || command2 == "user" || command2 == "role" || command2 == "tablespace" ) { //добавляем в выходной скрипт globals вызов безопасной функции-обёртки if (!helperInjected) { //но сначала вставляем команды создания функций-обёрток helperInjected = true; QListIterator helperIter(_createHelperFunctionsScript); while (helperIter.hasNext()) oa_globalsScript.append(helperIter.next()); } //переход на начало имени объекта 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; //т.к. имя без двойных кавычек, то перевести его в нижний регистр, //как это делает PostgreSQL name = line.mid( p, i - p ).toLower(); 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 safe_create_" ); + 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( originalLine ); } } else { //все остальные команды в основной скрипт oa_mainScript.append( originalLine ); } } //цикл по командам 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 ) { static const char* CREATE_SQL = "create language plpgsql"; static const char* CHECK_SQL = "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; }