Search code examples
firebirdfirebird1.5firebird-3.0

Firebird strip procedures, trggers views and udf


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.


Solution

  • 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