Question

We have certain environments that prefer the use of materialized views, but the regular applications use regular views. To make things easier we want our application to automatically migrate all the regular views to materialized views in an automated fashion, based upon a configuration parameter.

I've written what I consider to be the most of what I need to get this script working, but I am struggling on the final touches. Possibly there are some escaping issues that I need to work around too.

For now the script creates a view called 'magic' and then tries to convert that, but so far it's not working on the conversion to a materialized view stage. I am unsure what I've done wrong. Any help greatly appreciated.

The error I am seeing is as follows.

Error report:
ORA-00911: invalid character
ORA-06512: at line 49
ORA-00911: invalid character
00911. 00000 -  "invalid character"
*Cause:    identifiers may not start with any ASCII character other than
           letters and numbers.  $#_ are also allowed after the first
           character.  Identifiers enclosed by doublequotes may contain
           any character other than a doublequote.  Alternative quotes
           (q'#...#') cannot use spaces, tabs, or carriage returns as
           delimiters.  For all other contexts, consult the SQL Language
           Reference Manual.
*Action:
Attempting to drop materialized view named MAGIC
No materialized view found with name MAGIC
Attempting to drop view named MAGIC
Success.
Attempting to create materialized view named MAGIC
  CREATE MATERIALIZED VIEW "MYDB"."MAGIC" ("MAGIC") AS 
  SELECT 'MAGIC' FROM DUAL;

Failed to create materialized view, recreating original view MAGIC
ERROR: Could not recreate view named MAGIC.
SQL was:
  CREATE OR REPLACE FORCE VIEW "MYDB"."MAGIC" ("MAGIC") AS 
  SELECT 'MAGIC' FROM DUAL;

The PL/SQL code is below.

clear;
SET serveroutput ON size 1000000;
/**
* Converts all views in the database to materialized views.
*/

CREATE OR REPLACE VIEW "MAGIC" ("MAGIC") AS SELECT 'MAGIC' FROM DUAL;

BEGIN
  FOR cur_rec IN ( SELECT object_name, object_type FROM user_objects WHERE object_type='VIEW' and object_name='MAGIC' )
  LOOP
    BEGIN
      FOR cur_view IN
      (SELECT TRIM( REPLACE( REPLACE( DBMS_METADATA.GET_DDL('VIEW', cur_rec.object_name), 'CREATE OR REPLACE FORCE VIEW', 'CREATE MATERIALIZED VIEW' ), 'CREATE OR REPLACE VIEW', 'CREATE MATERIALIZED VIEW' ) ) "MATERIALIZED_VIEW",
        TRIM( DBMS_METADATA.GET_DDL('VIEW', cur_rec.object_name) ) "VIEW"
      FROM DUAL
      )
      LOOP
        BEGIN
          BEGIN
            DBMS_OUTPUT.PUT_LINE( 'Attempting to drop materialized view named ' || cur_rec.object_name );
            EXECUTE IMMEDIATE 'drop materialized view ' || cur_rec.object_name;
            DBMS_OUTPUT.PUT_LINE( 'Success.' );
          EXCEPTION
          WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE( 'No materialized view found with name ' || cur_rec.object_name );
            IF SQLCODE != -12003 THEN
              RAISE;
            END IF;
          END;
          BEGIN
            DBMS_OUTPUT.PUT_LINE( 'Attempting to drop view named ' || cur_rec.object_name );
            EXECUTE IMMEDIATE 'drop view ' || cur_rec.object_name;
            DBMS_OUTPUT.PUT_LINE( 'Success.' );
          EXCEPTION
          WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE( 'No view found with name ' || cur_rec.object_name );
            IF SQLCODE != -942 THEN
              RAISE;
            END IF;
          END;
          -- create the view as a materialized view.
          BEGIN
            DBMS_OUTPUT.PUT_LINE( 'Attempting to create materialized view named ' || cur_rec.object_name );
            DBMS_OUTPUT.PUT_LINE( cur_view."MATERIALIZED_VIEW" );
            EXECUTE IMMEDIATE cur_view."MATERIALIZED_VIEW";
            DBMS_OUTPUT.PUT_LINE( 'Success.' );
          EXCEPTION
          WHEN OTHERS THEN
            BEGIN
              DBMS_OUTPUT.PUT_LINE( 'Failed to create materialized view, recreating original view ' || cur_rec.object_name );
              EXECUTE IMMEDIATE cur_view."VIEW";
            EXCEPTION
            WHEN OTHERS THEN
              DBMS_OUTPUT.PUT_LINE( 'ERROR: Could not recreate view named ' || cur_rec.object_name || '.' );
              DBMS_OUTPUT.PUT_LINE( 'SQL was:' || cur_view."VIEW" );
              RAISE;
            END;
          END;
        END;
      END LOOP;
    END;
  END LOOP;
END;
Was it helpful?

Solution

The problem is with the SQL it's trying to execute:

CREATE MATERIALIZED VIEW "MYDB"."MAGIC" ("MAGIC") AS 
SELECT 'MAGIC' FROM DUAL;

The dynamic SQL should be a single statement and cannot have a statement terminator/separator; it's the final ; it doesn't like.

You can stop dbms_metadata including it in the DDL in the first place by adding a call to the set_transform_param() procedure in your block, before you call get_ddl():

dbms_metadata.set_transform_param(dbms_metadata.session_transform,
  'SQLTERMINATOR', false);

OTHER TIPS

UPDATE - Final Solution

The database was crashing server-side when running this script as some of my views depend on others. I get the impression that this is done in a multi-threaded fashion and that caused the server to crash and burn as views were in various states of readiness. From a sequential processing point of view, I can't see how this can fail. The dependant views exist either as a materialized view or a regular view.

Finally, it's possible these times could be reduced, these were the values that worked for me, but I've not tested lower thresholds at the time of writing this answer.

set serveroutput on size 1000000;
/**
* Converts all views in the database to materialized views.
*/
begin
  dbms_metadata.set_transform_param(dbms_metadata.session_transform, 'SQLTERMINATOR', false );
  for cur_rec in ( SELECT object_name, object_type from user_objects where object_type='VIEW' )
  loop
    begin
      for cur_view in
      (select trim( replace( replace( dbms_metadata.get_ddl( 'VIEW', cur_rec.object_name ), 'CREATE OR REPLACE FORCE VIEW', 'CREATE MATERIALIZED VIEW' ), 'CREATE OR REPLACE VIEW', 'CREATE MATERIALIZED VIEW' ) ) "MATERIALIZED_VIEW",
        trim( dbms_metadata.get_ddl( 'VIEW', cur_rec.object_name ) ) "VIEW"
      from dual )
      loop
        begin
          begin
            execute immediate 'drop materialized view ' || cur_rec.object_name;
            dbms_lock.sleep(5);
          exception
          when others then
            if sqlcode != -12003 then
              raise;
            end if;
          end;
          begin
            execute immediate 'drop view ' || cur_rec.object_name;
            dbms_lock.sleep(5);
          exception
          when others then
            if sqlcode != -942 then
              raise;
            end if;
          end;
          -- create the view as a materialized view.
          begin
            dbms_output.put_line( 'Attempting to create materialized view named ' || cur_rec.object_name );
            execute immediate cur_view."MATERIALIZED_VIEW";
            dbms_lock.sleep(5);
          exception
          when others then
            begin
              dbms_output.put_line( 'Failed to create materialized view, recreating original view ' || cur_rec.object_name );
              dbms_output.put_line( 'Error was: ' || sqlerrm( sqlcode ) );
              dbms_output.put_line( cur_view."MATERIALIZED_VIEW" );
              execute immediate cur_view."VIEW";
              dbms_lock.sleep(5);
            exception
            when others then
              dbms_output.put_line( 'ERROR: Could not recreate view named ' || cur_rec.object_name || '.' );
              dbms_output.put_line( 'SQL was:' || cur_view."VIEW" );
              raise;
            end;
          end;
        end;
      end loop;
    end;
  end loop;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top