Firebird strip procedures, trggers views and udf

319 views Asked by At

I am preparing a new version of my software, making the transition from Firebird 1.5 to 3. My installation program backs up the Firebird 1.5 database and restores it through the Firebird 3 server or embedded server depending on the installation type (local/multiuser). This all works well.

I want to recreate all procedures triggers and views using the new Firebird capabilities and try to get rid of UDFs I used in 1.5. Therefore I tried to delete all that stuff, but I stumbled upon the problem that I cannot drop, for example, a view that uses an UDF that doesn't exist in FB3. As the UDFs are not available with Firebird 3, I am kind of stuck.

Deleting those objects in the old database is no option as I don't want to destroy this fallback option. Also making two backup/restore rounds is not an option, because we are talking about quite big databases.

I need to let the installation program do all that because I cannot access al the customers systems.

1

There are 1 answers

0
MichaSchumann On BEST ANSWER

Thanks to Mark, I tried once more and eventually somehow got altering all views to "select 1 as test from rdb$database" working and then could delete them.

As I have many different versions of my schema in the field I am not exactly sure which dependencies excactly I will come across. So I wrote this PSQL Block, that iterates through all these objects ignoring errors until everything is cleaned up. So if any of these objects is not deletable I break out of the iteration once it has run 100 times to avoid hangs. After that I check if any procedure, view, trigger and function still exists. If so, I provoke an exception.

I know that this is kind of "dirty" solution (ignoring exceptions is usually a NoGo) but I have no better idea and as neither an endless loop nor undetected error can happen, I will use this way.

EXECUTE BLOCK
as
declare x integer;
declare y integer;
declare z integer;
declare s varchar(100);
declare s1 varchar(100);
begin
    x=1;
    y=0;
    while (x>0 and y<100) do
    -- we break out of the loop if we have more than 100 rounds as this indicates
    -- at least one object could not be deleted.
      begin
        y=y+1;
        for SELECT distinct RDB$VIEW_NAME from RDB$VIEW_RELATIONS into :s  do
          begin
            in autonomous transaction do
            execute statement 'alter view ' || s || ' as select 1 as test from rdb$database';
             -- Ignore errors here for now
            in autonomous transaction do
            execute statement 'drop view ' || s;
             -- Ignore errors here for now
            when any do begin end
          end
        for SELECT RDB$PROCEDURE_NAME from RDB$PROCEDURES into :s  do
          begin
            in autonomous transaction do
            execute statement 'drop procedure ' || s;
             -- Ignore errors here for now
            when any do begin end
          end

        for select RDB$TRIGGER_NAME from RDB$TRIGGERS  where RDB$SYSTEM_FLAG=0 into :s  do
          begin
           in autonomous transaction do
            execute statement 'drop trigger ' || s;
            -- Ignore errors here for now
            when any do begin end
          end
        for select RDB$FUNCTION_NAME from RDB$FUNCTIONS into :s  do
          begin
             in autonomous transaction do
             execute statement 'drop function ' || s;
              -- Ignore errors here for now
             when any do begin end
          end
        for select rdb$constraint_name,rdb$relation_name from RDB$RELATION_CONSTRAINTS  where not rdb$relation_name containing ('$') into :s,:s1  do
          begin
             in autonomous transaction do
             execute statement 'alter table ' || s1 || ' drop constraint ' || s;
              -- Ignore errors here for now
             when any do begin end
          end
        for select rdb$index_name from rdb$indices where rdb$system_flag=0 into :s  do
          begin
             in autonomous transaction do
             execute statement 'drop index ' || s;
              -- Ignore errors here for now
             when any do begin end
          end
       x = 0;
       SELECT count(*) from RDB$PROCEDURES into :z;
       x = x + z;
       SELECT count(distinct RDB$VIEW_NAME) from RDB$VIEW_RELATIONS into :z;
       x = x + z;
       select count(*) from RDB$TRIGGERS  where RDB$SYSTEM_FLAG=0 into :z;
       x = x + z;
       select count(*) from RDB$FUNCTIONS into :z;
       x = x + z;
       select count(*) from RDB$RELATION_CONSTRAINTS  where not rdb$relation_name containing ('$') into :z;
       x = x + z;
       select count(*) from rdb$indices where rdb$system_flag=0 into :z;
       x = x + z;
     end
     if (x>0) then
          -- Raise an exception showing that the block failed
          y=x/0;
 end

UPDATE: I added code to drop all constraints and indexes.

UPDATE 2: It might be a good idea to preserve "not null" constraints as they are only recreatable with a domain. To do so just change the select statement for constraints to:

 for select rdb$constraint_name,rdb$relation_name from RDB$RELATION_CONSTRAINTS
           where rdb$constraint_type<>'NOT NULL' and not rdb$relation_name containing ('$') into :s,:s1  do