Инкрементное обновление структуры базы данных
После каждого успешного обновления структуры БД заносится запись в таблицу FIN_VERSIONINFO. Таким образом, текущую версию структуры можно извлечь по максимальному идентификатору с помощью запроса:
SELECT FIRST 1 * FROM fin_versioninfo ORDER BY id DESC
Для изменения структуры БД разработчик создает процедуру типа TProcAddr. Как правило, на одно изменение создается одна процедура. Ниже приведен пример из файла mdf_ConvertBNStatementCommentToBlob:
procedure ModifyRUIDProcedure(IBDB: TIBDatabase; Log: TModifyLog); var FTransaction: TIBTransaction; FIBSQL: TIBSQL; begin FTransaction := TIBTransaction.Create(nil); FIBSQL := TIBSQL.Create(nil); try FTransaction.DefaultDatabase := IBDB; try FTransaction.StartTransaction; FIBSQL.Transaction := FTransaction; FIBSQL.ParamCheck := False; FIBSQL.SQL.Text := 'CREATE OR ALTER PROCEDURE GD_P_GETRUID(ID INTEGER) '#13#10 + ' RETURNS (XID INTEGER, DBID INTEGER) '#13#10 + 'AS '#13#10 + 'BEGIN '#13#10 + ' XID = NULL; '#13#10 + ' DBID = NULL; '#13#10 + ' '#13#10 + ' IF (NOT :ID IS NULL) THEN '#13#10 + ' BEGIN '#13#10 + ' IF (:ID < 147000000) THEN '#13#10 + ' BEGIN '#13#10 + ' XID = :ID; '#13#10 + ' DBID = 17; '#13#10 + ' END ELSE '#13#10 + ' BEGIN '#13#10 + ' SELECT xid, dbid '#13#10 + ' FROM gd_ruid '#13#10 + ' WHERE id=:ID '#13#10 + ' INTO :XID, :DBID; '#13#10 + ' '#13#10 + ' IF (XID IS NULL) THEN '#13#10 + ' BEGIN '#13#10 + ' XID = ID; '#13#10 + ' DBID = GEN_ID(gd_g_dbid, 0); '#13#10 + ' '#13#10 + ' INSERT INTO gd_ruid(id, xid, dbid, modified, editorkey) '#13#10 + ' VALUES(:ID, :XID, :DBID, CURRENT_TIMESTAMP, NULL); '#13#10 + ' END '#13#10 + ' END '#13#10 + ' END '#13#10 + ' '#13#10 + ' SUSPEND; '#13#10 + 'END '; FIBSQL.ExecQuery; FIBSQL.SQL.Text := 'UPDATE OR INSERT INTO fin_versioninfo ' + ' VALUES (132, ''0000.0001.0000.0163'', ''28.03.2011'', ''Modify GD_P_GETRUID procedure.'') ' + ' MATCHING (id)'; FIBSQL.ExecQuery; FIBSQL.Close; FTransaction.Commit; except on E: Exception do begin Log('Произошла ошибка: ' + E.Message); if FTransaction.InTransaction then FTransaction.Rollback; raise; end; end; finally FIBSQL.Free; FTransaction.Free; end; end;
В данном случае вносятся изменения в хранимую процедуру GD_P_GETRUID. Как видно, на вход передается коннект к базе данных и процедура обратного вызова для логирования. В общем случае, алгоритм обновления выглядит следующим образом:
- Создаются и связываются объекты транзакции и SQL команды
- Стартует транзакция
- Делаются необходимые проверки
- Выполняется одна или несколько SQL команд
- Помещаются сообщения в лог о ходе процесса
- Добавляется запись в fin_versioninfo о новом номере версии структуры БД
- Комитится транзакция
- Если произошла ошибка, то сообщение помещается в лог и транзакция откатывается
Все подобные процедуры вместе с номером версии структуры БД заносятся в глобальный массив cProcList. При запуске Гедымина, после аутентификации пользователя и подключения к базе, номер текущей версии структуры БД сверяется с номером из последнего элемента массива. Если номер текущей версии меньше или равен номеру из последнего элемента, то создается объект типа TgdModify и запускается процесс апгрейда.
В ходе обновления последовательно выполняются процедуры из массива cProcList, пока выполняется условие:
FDBVersion <= cProcList[I].ModifyVersion
где FDBVersion -- номер текущей версии структуры БД, cProcList[I].ModifyVersion -- номер версии, указанный для I-той процедуры.