Утилита конвертации базы данных из Yaffil в Firebird (постановка)
Ссылка на страничку утилиты FDBConvert.
Утилита предназначена для конвертации существующей базы данных из формата серверов Yaffil, FB 1.5, FB 2.0, FB 2.1 в FB 2.5. Конвертация возможна как из командной строки, так и в интерактивном режиме.
Утилита решает следующие проблемы:
- Апгрейд в новую структуру файла базы данных с конвертированием юникодовских данных (FIX_FSS_DATA, FIX_FSS_METADATA).
- Замена всех внешних функций, для которых теперь есть аналогичные встроенные.
- Замена вызовов функций GUDF.DLL на встроенные (например, G_B_AND => BIN_AND).
Алгоритм
Перед началом работы
- Проверяем наличие, комплектность и работоспособность встроенного сервера Firebird 2.5. Если нет, то сообщаем пользователю о необходимости переустановки программы и выходим.
Если на вход передано имя файла бэкапа базы данных
- Пытаемся восстановить из бэкапа базу данных применяя в указанной последовательности сервера: Yaffil, FB 1.5, FB 2.0, FB 2.1, FB 2.5.
- Если удалось восстановить БД, то выполняем алгоритм "Если на вход передано имя файла базы данных".
- Если не удалось восстановить, то сообщаем об ошибке.
Если на вход передано имя файла базы данных
- Подключаемся к БД с помощью ФБ 2.5 и определяем версию структуры БД.
- Отключаемся от базы.
- Если уже 2.5, то спрашиваем пользователя стоит ли продолжать?
- Пользователь может выбрать конвертацию базы FB 2.5, например, если ему надо заменить внешние функции на встроенные.
- В зависимости от определенной версии структуры БД проверяем наличие, комплектность и работоспособность встроенного сервера Firebird или Yaffil нужной нам версии.
- Если нет, то сообщаем пользователю о необходимости переустановки программы и выходим.
- Выводим пользователю подробную информацию о том, что мы будем сейчас делать:
- Полное имя исходной базы данных.
- Версия исходной базы данных.
- Версия исходного сервера.
- Версия нового сервера.
- Полное имя архивного файла. Поле доступно для редактирования. Например, пользователь может разместить файл на другом диске.
- Полное имя временной базы данных. Поле доступно для редактирования. Например, пользователь может разместить файл на другом диске.
- Логин и пароль для запуска утилиты gfix, gbak??
- Размер страницы новой базы данных и размер кэша в страницах. Пользователь может изменять значения. Если нам удастся получить значения из существующей базы, то выводим их в качестве значений по-умолчанию.
- Кодовая таблица для конвертации новой базы данных. Пользователь может выбрать значение из списка. Если нам удастся получить значение из существующей базы, то выводим его в качестве значения по-умолчанию.
- Список функций для замены. Пользователь может редактировать список.
- Если пользователь ознакомился со списком и нажал кнопку Конвертировать, то продолжаем.
- Определяем (приблизительно) потребность в дисковом пространстве для конвертации БД. Используем формулу: Размер БД * 1.5 (Учитываем пространство на копию БД + архив БД).
- Если недостаточно -- информируем пользователя. На этом шаге пользователь имеет возможность освободить место на диске и повторить проверку. Если места все равно недостаточно, то возвращаемся к предыдущему экрану, где пользователь может указать иное размещение файла с архивом.
- Создаем копию файла БД на которой будем выполнять преобразование. Если файл с таким именем уже существует, то спрашиваем пользователя о перезаписи.
- Вызываем через сервис проверку БД с ключами -full -mend для устранения возможных ошибок в структуре.
- Подключаемся сервером, соответствующим версии структуры БД.
- Составляем список внешних функций для которых есть зависимые объекты. Удаляем из списка функции, которые мы будем менять на встроенные в процессе конвертации. Для оставшихся определяем необходимые сторонние библиотеки и проверяем их наличие.
- Если нужной библиотеки нет в соответствующем подкаталоге со встроенным сервером, то сообщаем пользователю. Пользователь должен скопировать библиотеку из рабочего каталога своего сервера. После этого сканирование списка функций повторяется.
- Если пользователь не может найти и скопировать нужную библиотеку, то сообщаем и останавливаем процесс конвертации.
- Если нужной библиотеки нет в соответствующем подкаталоге со встроенным сервером, то сообщаем пользователю. Пользователь должен скопировать библиотеку из рабочего каталога своего сервера. После этого сканирование списка функций повторяется.
- Создаем временные таблицы, куда в порядке зависимости скидываем коды:
- триггеров (Так как в последствии мы не удаляем тело триггера, а только комментируем его, то нет смысла скидывать полный код. Достаточно запомнить имя триггера, чтобы в последствии знать что восстанавливать.),
- процедур (аналогично как и для триггера, см. выше),
- представлений,
- вычисляемых полей.
- Тела триггеров делаем закомментированным. Используем комментарий со специальной сигнатурой, чтобы корректно удалить его в будущем.
- Тела процедур делаем закомментированным. Используем комментарий со специальной сигнатурой, чтобы корректно удалить его в будущем.
- Представления удаляем.
- Вычисляемые поля удаляем.
- Удаляем из списка внешних функций все функции, которые теперь являются встроенными в сервер.
- Делаем бэкап базы (через сервис).
- Отключаемся от базы.
- Восстанавливаем базу из архива поверх сделанной копии с ключами -REP -FIX_FSS_DATA -FIX_FSS_METADATA, используя утилиту gbak[1] из комплекта сервера Firebird 2.5.
- Загружаем локальный ФБ 2.5.
- Воссоздаем вычисляемые поля.
- Воссоздаем представления в порядке зависимости.
- Восстанавливаем процедуры в порядке зависимости.
- Восстанавливаем триггеры.
- Удаляем промежуточный архивный файл.
- Переименовываем старый файл базы данных в *.BAK.
- Переименовываем преобразованный файл базы данных.
- Сообщаем пользователю. Завершаем процесс.
Обработка исходного кода вычисляемых полей, представлений, триггеров и процедур при восстановлении
В соответствии с заданным списком корректируем исходный код. Например, в списке задана замена:
GS_B_AND => BIN_AND LENGTH => CHAR_LENGTH
Мы будем искать в исходном коде все вызовы функций GS_B_AND и LENGTH и заменять их на BIN_AND и CHAR_LENGTH соответственно.
Наш механизм будет поддерживать только простой случай замены, когда входящие параметры заменяемой и новой функции совпадают. В более сложных случаях базу надо конвертировать вручную.
Сам список замены предлагается хранить в файле substitute.ini. Имя параметра -- это имя исходной функции, а значение -- новой, на которую меняем. Для доступа к файлу будем использовать функции WinAPI. Пример файла:
GS_B_AND = BIN_AND GS_B_OR = BIN_OR LENGTH = CHAR_LENGTH ...
Обработка ошибок при восстановлении
Если при восстановлении триггера/процедуры возникает ошибка, например из-за того, что программный код использовал особенности сервера Yaffil, которые не поддерживаются сервером Firebird 2.5, то сообщаем об этом пользователю и предлагаем два варианта:
- Прервать процесс обновления.
- Восстановить процедуру/триггер c закоментированным телом.
- Открыть текст процедуры/триггера на экране для того, чтобы пользователь мог внести в него исправления.
Если пользователь выбрал первое, то завершаем процесс и удаляем все временные файлы. Если второе -- по окончании процесса сообщаем ему список процедур/триггеров, которые необходимо исправить вручную.
Если при воссоздании вычисляемого поля или представления возникает ошибка, например из-за того, что программный код использовал особенности сервера Yaffil, которые не поддерживаются сервером Firebird 2.5, то сообщаем об этом пользователю и предлагаем:
- Прервать процесс обновления.
- Открыть текст поля/представления на экране для того, чтобы пользователь мог внести в него исправления.
Если пользователь выбрал прервать, не забываем удалиь все временные данные с диска.
Особенности реализации
Имя проекта
FDBCONVERT -- Converter of Firebird database files.
Разделение кода и интерфейса
Учитывая что мы предусматриваем два режима работы: из командной строки и интерактивно, ни в коем случае код не должен быть перемешан с интерфейсом.
Многопоточная архитектура
Чтобы не "замораживать" интерфейс пользователя на время выполнения длительных операций бэкапа/разбэкапа следует вынести их в отдельные нити.
Организация пользовательского интерфейса
Предлагается организовать интерфейс ввиде многостраничного пошагового мастера. Предположительное содержание страниц:
- Приветствие, выбор языка интерфейса.
- Выбор файла базы данных или файла архива для конвертации.
- Вывод подробной информации:
- исходный файл, его версия,
- конечный файл,
- исходная версия сервера,
- конечная версия сервера,
- место на диске,
- новый размер страницы,
- новый размер кэша,
- кодовая таблица символов.
- Вывод списка замены функций. Пользователь может удалять из списка функции, тогда они останутся в базе как есть сейчас.
- Вывод информации перед самым стартом. Ничего менять нельзя.
- Ход процесса.
- Завершение:
- Успешное. Объясняем пользователю где находится его новая база и где переименованный файл старой базы.
- Не успешное. Показываем лог ошибок. Объясняем, что его старая база осталась нетронутой. На всякий случай выводим где она находится.
- Страница с рекламой (и автоматическое открытие в браузере нашего сайта).
Структура метаданных
Это черновик!
CREATE TABLE CNV$DATA ( id INTEGER NOT NULL PRIMARY KEY, obj_type CHAR(1) NOT NULL, name VARCHAR(60) NOT NULL CHARACTER SET UNICODE_FSS, relation_name VARCHAR(60) CHARACTER SET UNICODE_FSS, source BLOB SUB_TYPE 1 CHARACTER SET UNICODE_FSS,
CHECK(obj_type IN ('T', 'P', 'F', V')) )
Для заполнения поля ID создадим генератор:
CREATE GENERATOR CNV$G_ID
и триггер:
CREATE TRIGGER CNV$BI_DATA FOR CNV$DATA BEFORE INSERT POSITION 0 AS BEGIN NEW.id = GEN_ID(CNV$G_ID, 1); END
Мультиязычная поддержка
Мультиязычная поддержка будет реализована следующим образом:
- В подкаталоге AppName (см. ниже структуру каталогов) будет располагаться файл languages.ini.
- Каждая секция файла содержит перевод экранных элементов для одного языка.
- Название секции -- название языка.
- Имя параметра -- это имя контрола или имя некоторого свойства.
- Значение параметра -- это метка (заголовок) на языке секции.
- Для доступа к содержимому файла мы будем использовать функции WinAPI.
- На первом экране программы (см. организацию пользовательского интерфейса) присутствует выпадающий список с названиями доступных языков. Список заполняется при старте программы, в результате сканирования файла languages.ini.
- При смене языка, метки (заголовки) экранных элементов заменяются значениями, прочитанными из файла languages.ini.
Пример содержимого файла languages.ini:
... [English] lblBkFile=Backup filename lblSourceDB=Source database ... [Russian] lblBkFile=Файл архива lblSourceDB=Исходная база данных ...
Структура каталогов программы
В соответствии со спецификацией Portable Apps:
AppNamePortable + App + AppInfo + AppName + YA887 + FB103 + FB155 + FB205 + FB250 + DefaultData + Data + Other + Help + Images + Source
Примечания
- ↑ Мы используем автономную утилиту вместо обращения к сервисам, так как последние не поддерживают упомянутые ключи.