В сообщении Принудительное удаление базы данных в PostgreSQL я писала о том, как принудительно закрыть все соединения с базой данных. Это удобно, когда есть «висящие» соединения, которые никак не хотят завершаться. Или нужно срочно удалить базу данных и нет времени выяснять, кто забыл от нее отключиться 🙂 Сегодня я хочу показать, как прервать соединения IP с базой данных.
Задача такая же: Есть объект QGroupBox groupBox, в него надо запихнуть виджеты (labels & lineEdits) в QGridLayout лэйауте. Данные берутся из запроса (он представлен в сообщении по ссылке выше).
Более универсальный способ будет выглядеть так:
if(ui->groupBox->layout()) {
QLayoutItem *child;
while ((child = ui->groupBox->layout()->takeAt(0)) != 0) {
delete child->widget();
delete child;
}
delete ui->groupBox->layout();
}
QGridLayout *layout = new QGridLayout(ui->groupBox);
ui->groupBox->setLayout(layout);
int pos = 0; //устанавливаем позицию расположения виджетов в 0
int colsCount = 2; //Количество колонок в лэйауте
while (query.next()) { //берем результаты запроса
QLabel *newLabel = new QLabel(this);
newLabel->setText(query.value("caption").toString()+":");
newLabel->setObjectName("label_"+query.value("name_").toString());
layout->addWidget(newLabel,pos/colsCount,pos%colsCount);
pos++; //сдвигаемся
QLineEdit *newEdit = new QLineEdit(this);
newEdit->setObjectName(query.value("name_").toString());
layout->addWidget(newEdit,pos/colsCount,pos%colsCount);
lineEdits.push_back(newEdit);
pos++;
}
В данном случае мне необходимо было разместить объекты в бокс с двумя колонками. Поэтому colsCount = 2. Если их должно быть больше, то значение переменной нужно поменять. Очищение лэйаута позволяет при изменении результатов запроса все очистить и нарисовать все в соответствии с актуальными данными.
Сегодня хочу познакомить вас с утилитой DBServerWizard, написание которой закончила намедни. Если коротко, ее предназначение — самые базовые элементы управления базами данных PostgreSQL.
Меню
Установка сервера
Бэкап/рестор
Создание БД
А если расписать немного подробнее, то перечень такой:
Установка сервера PostgreSQL (включает предварительную проверку наличия уже установленного сервера).
Создание и удаление баз данных. Есть некоторый обязательный набор баз данных; при подключении к серверу происходит проверка наличия этого минимального набора, если какие-либо БД из этого списка отсутствуют, то предлагается произвести создание БД и восстановление из бэкапа. Доступно принудительное удаление при наличии «застрявших» подключений, мешающих операции.
Резервное копирование и восстановление из копии. Перед восстановлением базы данных производится резервное копирование «на всякий случай».
Обновление баз данных из скриптов. Скрипт разделяется на отдельные инструкции, которые выполняются последовательно. Невыполненные инструкции записываются в отдельный файл.
Следующие сообщения были написаны в течение работы над утилитой:
Один из блогеров, ролики которого я иногда смотрю, всех приветствует примерно похожей фразой: «Hello, friends! Uuuhhh you look so beautiful today, totally stunning!» 🙂 Почти повторю и поздороваюсь (это почему-то не стало привычкой в моем недоблоге): «Доброго дня вам, мои дорогие друзья!»
Время от времени делаю усиленные попытки не забывать английский язык и смотрю/слушаю иностранные ролики. А совмещать с чем-то реально интересным для меня — польза вдвойне 🙂 Сегодня смотрела/слушала интервью — хоть и пятилетней давности — с вокалистом группы Starset Дастином Бэйтсом. Знакома с этим коллективом больше года наверное, но сподобилась побольше их послушать только сейчас. При прослушивании песни для каждого человека какой-то ее аспект будет играть главенствующую роль: насколько быстр барабанщик, насколько тяжелы гитарные рифы, каков мотивчик в целом, голос вокалиста(ов), смысл текста. Сначала прислушиваюсь к голосу. Если он мне приятен, хочется прочувствовать, о чем же там поется, а если смысла особо и нет, то мне и не интересно. А потом «зацениваю» всю аранжировку в целом. Это касается рок-групп в широком смысле этого слова. Стоит ли объяснять, что в Starset для моего восприятия все гениально сложилось вместе? Невероятно привлекательный вокал + тексты + инструменты (скрипка + электроника + гитары — так можно было? — невообразимо гармоничное сочетание). А переходы от спокойного вокала к агрессиву с хрипотцой? 💗 Была бы я 14-летней девочкой, завесила бы всю свою комнату плакатами Starset и Дастина Бэйтса 😁. Но я же серьезная женщина, поэтому Starset — неизменный спутник моих плейлистов сейчас.
Так вот в этом интервью он рассказывает и о самой группе, и кто вдохновляет на написание песен, и о смысле деятельности, композиций. Приятно послушать богатую и грамотную речь. Рекомендую 🙂
Сейчас речь пойдет о восстановлении базы из бэкапа. Здесь тоже будем использовать пакетный файл для вызова pg_restore.
Про каталог bin, файл паролей pgpass написано в статье по ссылке выше.
//name - имя базы данных
//file - файл резервной копии
void myClass::restoreDatabase(QString name, QString file)
{
QString errorMsg;
QString connectionName = connectToDB(name, errorMsg);
if (connectionName.isEmpty()){
error(errorMsg);
return;
}
QString binDir = postgresBinDirectory();
if (binDir.isEmpty()) {
error(tr("Системный каталог bin сервера не найден. Операция "
"восстановления базы данных %1 из резервной копии %2 прервана")
.arg(name).arg(file));
QSqlDatabase::removeDatabase(connectionName);
return;
}
QSqlQuery query(QSqlDatabase::database(connectionName));
//сначала удаляем схему (у меня паблик),
//затем ее снова создаем - чистенькую
if (!query.exec(QString("DROP SCHEMA public cascade; "
"CREATE SCHEMA public "
"AUTHORIZATION postgres; "
"COMMENT ON SCHEMA public "
"IS 'standard public schema'; "
"GRANT ALL ON SCHEMA public TO PUBLIC; "
"GRANT ALL ON SCHEMA public TO %1;")
.arg(postgresUser))) {
error(tr("Ошибка удаления схемы базы данных %1: %2")
.arg(name).arg(query.lastError().text()));
QSqlDatabase::removeDatabase(connectionName);
return;
}
QSqlDatabase::removeDatabase(connectionName);
//текст batch-файла
QString batText = QString("@ECHO OFF \n"
"SET dmpfile=%1 \n"
"set text = Begin to restore... \n"
"echo %text% \n"
"cd /d %2 \n"
"pg_restore --host=%4 --port=%5 "
"--username=%6 --verbose -d "
" %3 \"%dmpfile%\"\n"
"set /p id=\"The process is finished successfully. "
"Press Enter to exit...\"")
.arg(file, binDir, name,
host, ui->port->text(), postgresUser);
QString fileName = QString("%1-restore.bat")
.arg(name);
if (!writeBatFile(batText, fileName,
QCoreApplication::applicationDirPath()+"/temp"))
return;
//обновляем файл паролей, если нужно
updatePgPassFile(QString("%1:%2:%3:%4:%5")
.arg(host,
ui->port->text(),
name,
postgresUser,
ui->password->text()));
//вызываем пакетный файл для выполнения восстановления базы данных
system(qPrintable("cmd.exe /c \""+
QDir::toNativeSeparators(
QString("%1/%2")
.arg(QCoreApplication::applicationDirPath()+"/temp")
.arg(fileName)+"\"")));
}
Пример того, как можно реализовать резервное копирование базы данных PostgreSQL в Qt. Создается batch-файл, где вызывается утилита pg_dump.
//QString name - имя базы данных для создания копии
//file - "куда складывать" бэкап
bool myClass::backupDatabase(QString name, QString file)
{
QString binDir = postgresBinDirectory();
if (binDir.isEmpty()) {
error(tr("Системный каталог bin сервера не найден. "
"Операция восстановления базы данных "
"%1 из резервной копии %2 прервана")
.arg(name).arg(file));
return false;
}
if (file.isEmpty()) {
QDir dir(QCoreApplication::applicationDirPath()+
"/db_dumps/");
if (!dir.exists())
dir.mkpath(QCoreApplication::applicationDirPath()+
"/db_dumps/");
file = QString("%1/%2-%3.dump")
.arg(QCoreApplication::applicationDirPath()+
"/db_dumps")
.arg(name)
.arg(QDateTime::currentDateTime()
.toString("yyyyMMddhhmm"));
}
QString text = QString(
"@ECHO OFF \n"
"cd /d %1 \n"
"pg_dump -Fc -U %4 -Z 9 -v %2 > \"%3\" \n"
"set /p id=\"Press Enter to exit...\"\n")
.arg(binDir)
.arg(name)
.arg(file)
.arg(postgresUser);
QString fileName = QString("%1-dump.bat")
.arg(name);
//запись Значения строки text в файл *.bat
if (!writeBatFile(text, fileName,
QCoreApplication::applicationDirPath()+"/temp"))
return;
//обновление файла паролей pgpass
updatePgPassFile(QString("%1:%2:%3:%4:%5")
.arg(host,
ui->port->text(),
name,
postgresUser,
ui->password->text()));
//вызов созданного batch файла
system(qPrintable("cmd.exe /c \""+
QDir::toNativeSeparators(
QString("%1/%2")
.arg(QCoreApplication::applicationDirPath()+
"/temp")
.arg(fileName)+"\"")));
//если файл бэкапа имеет ненулевой размер
return QFileInfo(file).size() > 0;
}
Как узнать путь к каталогу bin сервера PostgreSQL, подробно написано здесь: Путь к каталогу bin PostgreSQL Windows. При условии известности имени пользователя и его пароля можно запустить утилиту pg_dump без необходимости авторизации пользователем при наличии соответствующей строки подключения в файле паролей pgpass, про что подробно писала в этой статье: Файл паролей PostgreSQL pgpass в Windows. В вышеприведенном коде используется функция updatePgPassFile(QString), код которой можно найти по обозначенной ссылке.