Здравствуйте, уважаемые хабралюди!

Порывшись в сети и в частности на Хабре не обнаружил ни одной темы как безболезненно и главное с 99% успеха обновлять базы данных.

Всем кто работал с БД известно, что основная проблема при обновлении БД — это зависимости между объектами.
Начинаешь удалять(пересоздавать процедуру), а у нее куча ссылок и без устранения зависимостей ничего не получится.
К тому же необходимо еще правильно написать скрипт обновления БД, и проверить его раз семь прежде чем отправлять клиенту, иначе не трудно догадаться что может случиться с его базой :) Плюс ко всему вряд ли кто пишет скрипты обновления с любой версии на последнюю. Как правило это целый ряд скриптов которые необходимо запускать в определенной последовательности.

Для того чтобы решить эти проблемы необходимо применять следующую методику:

1. Удалить все объекты(кроме таблиц)
2. Выполнить действия по обновлению структуры стаблиц, данных
3. Пересоздать все объекты(уже новой версии)

Данная методика как раз позволяет обновить БД с любой версии до последней, но возникает вопрос: как безопасно удалить все объекты?
Можно конечно написать скрипт который будет учитывать связи между объектами и удалять их в нужной последовательности. Такой подход трудоемок и при модификации БД требует корректировки скрипта удаления объектов, к тому же существует проблема с удалением рекуррентных процедур.
Лучше и быстрее избавляться от связей в процедурах пересоздав их с теми же входными и выходными параметрами, но с пустым телом. При этом зависимости пропадут сами собой.
Вот пример скрипта который как раз этим и занимается(писался и тестировался для FireBird 2.0 и выше)

Блок 1 — Пересоздаем все процедуры с пустым телом
execute block
as
declare variable lPROC_NAME varchar(31);
declare variable lPARAM_NAME varchar(31);
declare variable lPARAM_NUMBER smallint;
declare variable lPARAM_COUNT_INP smallint;
declare variable lPARAM_COUNT_OUT smallint;
declare variable lPARAM_TYPE smallint;
declare variable lTEXT varchar(5000);
declare variable lTEXT_INP varchar(2500);
declare variable lTEXT_OUT varchar(2500);
declare variable lSUB_TEXT varchar(50) = ' as begin end' ;
declare variable lFIELD_SOURCE varchar(31);
declare variable lVARIABLE varchar(100);
begin
for select P.RDB$PROCEDURE_NAME, P.RDB$PROCEDURE_INPUTS, P.RDB$PROCEDURE_OUTPUTS from RDB$PROCEDURES P
into :lPROC_NAME, :lPARAM_COUNT_INP, :lPARAM_COUNT_OUT
do
begin
lTEXT = 'create or alter procedure '||trim(:lPROC_NAME);
lTEXT_INP = '';
lTEXT_OUT = '';
if (coalesce(lPARAM_COUNT_INP,0)>0) then
lTEXT = lTEXT||'(';
if (coalesce(lPARAM_COUNT_OUT,0)>0) then
lTEXT_OUT = 'returns( ';
for
select PP.RDB$PARAMETER_NAME, PP.RDB$PARAMETER_NUMBER,
PP.RDB$PARAMETER_TYPE, PP.RDB$FIELD_SOURCE
from RDB$PROCEDURE_PARAMETERS PP
where PP.RDB$PROCEDURE_NAME = trim(:lPROC_NAME)
order by PP.RDB$PARAMETER_NUMBER, PP.RDB$PARAMETER_TYPE
into :lPARAM_NAME, :lPARAM_NUMBER,
:lPARAM_TYPE, :lFIELD_SOURCE
do
begin
lPARAM_NAME = trim(lPARAM_NAME);
lFIELD_SOURCE = trim(lFIELD_SOURCE);
select
case
when F.RDB$FIELD_TYPE = 261 then :lPARAM_NAME||' blob sub_type '||F.RDB$FIELD_SUB_TYPE||' segment size '||F.RDB$SEGMENT_LENGTH
when F.RDB$FIELD_TYPE = 14 then :lPARAM_NAME||' char('||F.RDB$FIELD_LENGTH||')'
when F.RDB$FIELD_TYPE = 40 then :lPARAM_NAME||' cstring('||F.RDB$FIELD_LENGTH||')'
when F.RDB$FIELD_TYPE = 11 then :lPARAM_NAME||' dfloat'
when F.RDB$FIELD_TYPE = 27 then :lPARAM_NAME||' double'
when F.RDB$FIELD_TYPE = 10 then :lPARAM_NAME||' float'
when F.RDB$FIELD_TYPE = 16 and coalesce(F.RDB$FIELD_SCALE,0) = 0 then :lPARAM_NAME||' bigint'
when F.RDB$FIELD_TYPE = 8 and coalesce(F.RDB$FIELD_SCALE,0) = 0 then :lPARAM_NAME||' integer'
when F.RDB$FIELD_TYPE = 7 and coalesce(F.RDB$FIELD_SCALE,0) = 0 then :lPARAM_NAME||' smallint'
when F.RDB$FIELD_TYPE = 9 then :lPARAM_NAME||' quad'
when F.RDB$FIELD_TYPE = 12 then :lPARAM_NAME||' date'
when F.RDB$FIELD_TYPE = 13 then :lPARAM_NAME||' time'
when F.RDB$FIELD_TYPE = 35 then :lPARAM_NAME||' timestamp'
when F.RDB$FIELD_TYPE = 37 then :lPARAM_NAME||' varchar('||F.RDB$FIELD_LENGTH||')'
when coalesce(F.RDB$FIELD_SCALE,0) <> 0 then :lPARAM_NAME||' numeric('||F.RDB$FIELD_PRECISION||','||iif(F.RDB$FIELD_SCALE<0, -F.RDB$FIELD_SCALE, F.RDB$FIELD_SCALE)||')'
end
from RDB$FIELDS F
where F.RDB$FIELD_NAME = :lFIELD_SOURCE
into :lVARIABLE;
if (lPARAM_TYPE = 0) then --входные параметры
begin
lTEXT_INP = lTEXT_INP||' '||lVARIABLE||',';
if (lPARAM_NUMBER = lPARAM_COUNT_INP - 1) then
lTEXT_INP = substring(lTEXT_INP from 1 for char_length(lTEXT_INP) - 1)||')';
end
else --выходные переметры
begin
lTEXT_OUT = lTEXT_OUT||' '||lVARIABLE||',';
if (lPARAM_NUMBER = lPARAM_COUNT_OUT - 1) then
lTEXT_OUT = substring(lTEXT_OUT from 1 for char_length(lTEXT_OUT) - 1)||')';
end
end
lTEXT = lTEXT||coalesce(lTEXT_INP,'')||' '||coalesce(lTEXT_OUT,'')||lSUB_TEXT;
execute statement lTEXT;
end
end;


Блок 2 — Удаляем все процедуры

execute block
as
declare variable lPROC_NAME varchar(31);
begin
for select P.RDB$PROCEDURE_NAME from RDB$PROCEDURES P
into :lPROC_NAME
do
execute statement 'drop procedure '||:lPROC_NAME;
end;

Все остальные объекты удаляются аналогично как в блоке 2.
Дальше нам необходим набор из скриптов(например пересоздения ВСЕХ представлений, внешних функций и процедур) в которых будет храниться актуальная на данный момент информация.
Запускаем их и получаем БД самой последней версии.
P.S. Все скрипты тестились неоднократно и прекрасно работают на FireBird, немного поработать напильником и можно адаптировать под любую СУБД.

Спасибо за внимание.