Dropping Virtual Columns Causes Unnecessary Invalidation

When you drop a column from a table (or set a column unused), any view that references this column becomes invalid.
As of Oracle 11gR1, where Fine Grained Dependency Tracking was introduced, views that reference other columns of the table, but do not reference the dropped column, should not become invalid. And this is usually true, but this week I discovered a case where it’s not.

When dropping a virtual column, even views that do not reference the dropped column become invalid.

Let’s see an example (tested in 11g, 12c and 18c):

We create a table with 3 columns, one of them is virtual

ORA$BASE> create table t (
  2    column_in_view             number,
  3    regular_column_not_in_view number,
  4    virtual_column_not_in_view number as (42) virtual
  5  );

Table created.

And we create a view that projects only one of the table columns

ORA$BASE> create or replace view v as
  2    select column_in_view from t;

View created.

The view is valid at this point

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
VALID

Dropping the non-referenced non-virtual column does not invalidate the view (which is a good thing, of course)

ORA$BASE> alter table t drop column regular_column_not_in_view;

Table altered.

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
VALID

However, dropping the non-referenced virtual column does invalidate the view

ORA$BASE> alter table t drop column virtual_column_not_in_view;

Table altered.

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
INVALID

And we can see the same behavior when setting the columns unused instead of dropping them:

ORA$BASE> drop table t;

Table dropped.

ORA$BASE> create table t (
  2    column_in_view             number,
  3    regular_column_not_in_view number,
  4    virtual_column_not_in_view number as (42) virtual
  5  );

Table created.

ORA$BASE> create or replace view v as
  2    select column_in_view from t;

View created.

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
VALID

ORA$BASE> alter table t set unused column regular_column_not_in_view;

Table altered.

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
VALID

ORA$BASE> alter table t set unused column virtual_column_not_in_view;

Table altered.

ORA$BASE> select status from user_objects where object_name = 'V';

STATUS
-------
INVALID

ORA$BASE>

When using EBR (Edition-Based Redefinition) for online application upgrades, Fine Grained Dependency Tracking is very important. The common practice for dropping a column with no downtime is as follows:

  • In a new edition: take the column out of the editioning view that covers the table (and remove any references to it from the code)
  • Expose the new edition
  • As soon as all the sessions use the new edition: drop the column from the table (or set it unused)

This practice fails when the dropped column is a virtual one, due to the behavior we’ve seen above (which, in my opinion, is a bug). Until this bug is solved, I see two workarounds (which I don’t really like):

  • Just leave the virtual column there, unexposed to the editioning view
  • Create a new table (without that column) to replace the existing one (with the necessary migration work, including cross-edition triggers)

I’ll write more about this, and about other invalidation cases, in my EBR series.

4 thoughts on “Dropping Virtual Columns Causes Unnecessary Invalidation”

  1. “Drop column” is not a non-blocking operation. Moreover, it takes time—and this grows with the size of the table. You said “As soon as all the sessions use the new edition: drop the column from the table (or set it unused)”. No! when your goal is zero-downtime, you must simply say “no” to “drop column”. “Set unused” is your only option.

    1. I had a feeling you’d say that, Bryn 🙂
      Still, the invalidation is my big concern in this case…
      Thanks,
      Oren.

      1. Not entirely convinced Bryn took the time to read your post properly. Because “Moreover, it takes time—and this grows with the size of the table” is false for a virtual column.

        SQL> alter table t add x generated always as ( object_id+0 );
        
        Table altered.
        
        SQL> set timing on
        SQL> alter table t drop column owner;  --- normal col
        
        Table altered.
        
        Elapsed: 00:00:00.76
        SQL> alter table t drop column x;  -- virtual col
        
        Table altered.
        
        Elapsed: 00:00:00.03
        

        Back to the original topic – I think its perhaps a tougher proposition for virtual columns, because it would we would have to parse the expression to identify the specific column(s) that are impacted. I’m not saying its impossible…but its not trivial.

        eg, you cannot just scan for the column name 🙂

        SQL> create or replace
          2  function created(x date) return date deterministic is
          3  begin
          4    return x;
          5  end;
          6  /
        
        Function created.
        
        SQL> alter table t add x generated always as ( created(created(created(created))));
        
        Table altered.
        
        1. Connor, thanks for your comments!

          I believe Bryn was referring to my general statement about “the common practice for dropping a column with no downtime”, and not to the specific case of virtual columns.

          As the main pain this issue causes is with online upgrades, I’ll be satisfied even if it is fixed only for Editioning Views. And this should be easy, using USER_EDITIONING_VIEW_COLS.

          Thanks,
          Oren.

Leave a Reply

Your email address will not be published. Required fields are marked *