1

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.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
MichaSchumann
  • 1,361
  • 1
  • 17
  • 36
  • Have you tried using [`CREATE OR ALTER VIEW`](http://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-ddl-view.html#fblangref25-ddl-view-crtoralter)? Alternatively, you can make those UDF available under Firebird 3 just for the migration. – Mark Rotteveel Sep 21 '16 at 11:41
  • Any delete or alter command on the views using those udfs fail with invalid blr. Unfortunately the udfs are not available for FB3 as I stated above. I would love to replace them but as they are used, no avail. – MichaSchumann Sep 21 '16 at 11:49
  • As far as I am aware you should be able to use the existing UDFs that you also use with Firebird 1.5. – Mark Rotteveel Sep 21 '16 at 12:02
  • In this particular case I used AshocUDF that do run with Server 3 but hang and crash when using 3 in embedded mode. I already talked to the kind author but there is currently no solution in sight and I have to move forward. I can replace all udfs I used by psql functions as soon, i can get rid of them... – MichaSchumann Sep 21 '16 at 12:07
  • I suggest you post to the firebird-support mailinglist, maybe one of the core developers has an idea how to do this. To be honest: it sounds like a bug to me. Other solutions would be to create a database from scratch and pump the data. – Mark Rotteveel Sep 21 '16 at 12:15
  • Thanks, very good idea. But - I solved it. – MichaSchumann Sep 21 '16 at 12:19

1 Answers1

1

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
MichaSchumann
  • 1,361
  • 1
  • 17
  • 36