Error cannot alter type of a column used by a view or rule

I am trying to update a column of type integer to numeric(2) in postgres ALTER TABLE employee_status ALTER COLUMN status TYPE numeric(2); but getting the error ERROR: cannot alter type of a c...

I am trying to update a column of type integer to numeric(2) in postgres

ALTER TABLE employee_status
    ALTER COLUMN status TYPE numeric(2);

but getting the error
ERROR: cannot alter type of a column used by a view or rule
DETAIL: rule _RETURN on view v_employee_details depends on column «status»

Without dropping or deleting data, how can i achieve this? is there any way to disable the rule or the view

Timur Shtatland's user avatar

asked Jul 8, 2020 at 11:04

not-a-bug's user avatar

The only possible way is to drop and re-create the view.

But that is no problem, you can easily get the view definition with the pg_get_viewdef function.

If you have a lot of dependent views, take a look at this answer to get all dependent views in the correct order.

Don’t worry about the _RETURN rule: that is just an implementation detail of how views are implemented in PostgreSQL: as ON SELECT DO INSTEAD rule named _RETURN.


do $$            
  declare v_employee_details_def text;
  declare exec_text text;
begin          
  v_employee_details_def := pg_get_viewdef('v_employee_details');
  drop view v_employee_details;
  
  -- do your other stuff
  
  exec_text := format('create view v_employee_details as %s', 
      v_employee_details_def);
  execute exec_text;
end $$;

senjin.hajrulahovic's user avatar

answered Jul 8, 2020 at 11:28

Laurenz Albe's user avatar

Laurenz AlbeLaurenz Albe

192k17 gold badges177 silver badges233 bronze badges

2

I found one more solution more simple, but may be not a best practice.

UPDATE pg_attribute SET atttypmod = 35+4 WHERE attrelid = 'TABLE1'::regclass AND attname = 'COL1';

You need a SUPERUSER access or similar to change Postgres system table — pg_attribute.
Here’s explanation link!

answered Aug 24, 2021 at 13:32

Alex's user avatar

AlexAlex

213 bronze badges

In a lectur on PostgreSQL a participant came up with a puzzling question: how to cascade an ALTER COLUMN from a table to a view? There are two main approaches: using the power of DDL transactionable commands or alter the system catalog. In this post I explain how to do both.

Imagine you have a table and a view (either dynamic or materialized) built on top of it:

> CREATE TABLE t( pk smallint, t char(2) );
> CREATE VIEW vw_t AS SELECT * FROM t;

and of course, both the view and the table reflect the same field structure

> d t
                    Table "public.t"
 Column |     Type     | Collation | Nullable | Default 
--------|--------------|-----------|----------|---------
 pk     | smallint     |           |          | 
 t      | character(2) |           |          | 

> d vw_t
                   View "public.vw_t"
 Column |     Type     | Collation | Nullable | Default 
--------|--------------|-----------|----------|---------
 pk     | smallint     |           |          | 
 t      | character(2) |           |          | 

What happens if the t table changes the structure? PostgreSQL does not allow this since there is a dependency from vw_t over t:

> ALTER TABLE t ALTER COLUMN t TYPE char(10);
ERROR:  cannot alter type of a column used by a view or rule
DETAIL:  rule _RETURN on view vw_t depends on column "t"

> ALTER TABLE t ALTER COLUMN pk TYPE bigint;
ERROR:  cannot alter type of a column used by a view or rule
DETAIL:  rule _RETURN on view vw_t depends on column "pk"

The DETAIL message provides an hint on the problem: the "_RETURN" rule is the special way PostgreSQL handles views: it does bounce the `SELECT** statement to the underlying table.

There is only a correct approach to solve the problem: since PostgreSQL allows any DDL command to be included in a transaction, a transaction can be used to drop the view, alter the table and recreate the view:

> BEGIN;
> DROP VIEW vw_t;
> ALTER TABLE t ALTER COLUMN pk TYPE bigint;
> ALTER TABLE t ALTER COLUMN t TYPE char(10);
> CREATE VIEW vw_t AS SELECT * FROM t;
> COMMIT;

Since views do not need to be compiled, as in other database systems, there should be never the case to not execute the above on a live system. In the very rare case where there is no possibility to start the above transaction, the system catalog can be changed to reflect the wanted structure.
Inspecting the pg_attribute system catalog shows how the table has been defined:

> SELECT attname, atttypmod, attlen 
  FROM pg_attribute
  WHERE attname IN ( 'pk', 't' )
  AND attrelid = 't'::regclass;
  
 attname | atttypmod | attlen 
---------|-----------|--------
 pk      |        -1 |      2
 t       |         6 |     -1

In short, the pk column being a number has no type modifiers (atttypmod) and a total length (attlen) of 2 bytes (it is a smallint); on the other hand, the t column has no specific length but a modifier of 6, in particular 4 added by the length itself (in this case, being a char(2) is 2 + 4 = 6).

Let’s say the table must to be changed so that the pk becomes a bigint and the t column a char(10): it is possible to force it on the catalog itself. While it is possible to modify the t column directly on the catalog increasing the type modifier length, this is not possible on the pk column because the pg_attribute.attlen is not a real value rather a copy of pg_type.typlen as shown in the pg_attribute documentation.
Therefore, the update of the t table has to be done as follows:
1) change the type of the numeric column pk;
2) change the type modifier length of the column t, adding to the desired value 4 bytes required by PostgreSQL bookeping.

The above two steps must be performed as a superuser, so if you cannot gain superuser privileges, you cannot update the table structure via the system catalog. It is also important to note that the names in pg_type are not SQL names, but PostgreSQL internal names; in other words the bigint type is named int8.

Let’s see the procedure in action:

# BEGIN; 
# UPDATE pg_attribute 
  SET atttypid = 
   ( SELECT oid 
     FROM pg_type 
     WHERE typname = 'int8' ) 
  WHERE attname = 'pk'
  AND attrelid = 't'::regclass;

# UPDATE pg_attribute SET atttypmod = 14 
  WHERE attname = 't'
  AND attrelid = 't'::regclass;

# d t
                    Table "public.t"
 Column |     Type      | Collation | Nullable | Default 
--------|---------------|-----------|----------|---------
 pk     | bigint        |           |          | 
 t      | character(10) |           |          | 

# d vw_t
                   View "public.vw_t"
 Column |     Type     | Collation | Nullable | Default 
--------|--------------|-----------|----------|---------
 pk     | smallint     |           |          | 
 t      | character(2) |           |          | 

-- if ready commit changes ...

Of course, this is not what is required, so I strongly discourage to commit the above transaction. There is the need to replay the very same statements against the whole dependent objects (in this case vw_t). If the view remains at its old structure odd behaviors can arise, since extracted values can be regularly read (i.e., the t column will be limited from the underlying table, so a char(10)) but updates of the view can result in data truncation.

Again, in PostgreSQL the correct way to perform such structure change is using an ordinary transaction, dropping the dependent objects and recreating them within the same transaction once the table has changed.

Take extreme care when working with the system catalog, and ensure you have a valid backup of your data before changing it, since this is not the intended way to let PostgreSQL protect your data!

The article PostgreSQL: cannot alter type of a column used by a view or rule
has been posted by
Luca Ferrari

on June 25, 2018

There are a handful of tables where CopyStorm uses database views rather than tables. A good example is ActivityHistory (a view combining the Task and Event tables).  If Salesforce makes a change which alters the type of length of a column in a table and the column is used in an existing view, PostgreSQL will throw an error like the following when CopyStorm tries to make the change in the database.

  • org.postgresql.util.PSQLException: ERROR: cannot alter type of a column used by a view or rule
    Detail: rule _RETURN on view openactivity depends on column “status”

The process to fix the issue is to:

  • Drop the views that depend on the table that needs modification
  • Run CopyStorm — it will make the table modification and automatically recreate the views.

Example From Summer’20

In Summer’20 Salesforce increased then length of the column Task.Status. Since the Task.Status column is used in CopyStorm views the update to Task.Status failed with:

  • org.postgresql.util.PSQLException: ERROR: cannot alter type of a column used by a view or rule
    Detail: rule _RETURN on view openactivity depends on column “status”

Here are the steps to fix this problem.

Step 1: Drop the Views Which Depend on Task.Status

Step 2: Run CopyStorm

The next time CopyStorm runs it will:

  • Successfully ALTER the Task.Status column because the dependent views no longer exist.
  • Recreate the views after detecting they have been deleted.

@leweafan

When I’m trying to change column type I’m getting an error «cannot alter type of a column used by a view or rule». Is there a work around? Can I rollback all views before changing and get it back after?

@calebhearth

Yeah you should be able to drop the view(s) that cause that issue, change the column, and re-add the views (use version specifier).

On Tue, Aug 23, 2016 at 3:35 AM leweafan

<
mailto:leweafan notifications@github.com

wrote:

a, pre, code, a:link, body { word-wrap: break-word !important; }

When I’m trying to change column type I’m getting an error «cannot alter type of a column used by a view or rule». Is there a work around? Can I rollback all views before changing and get it back after?

You are receiving this because you are subscribed to this thread.

Reply to this email directly,
#166
, or
https://github.com/notifications/unsubscribe-auth/AArcJcRdVp0YJ4xPUGh4q9HaRxW0Y3QCks5qirDsgaJpZM4Jqrcz
.

@leweafan

From gem description I see that if I want to drop views I should create a migration using drop_view.
And it’s not clear how I’ll get my views back.

@derekprior

This error is from postgres because postgres does not allow you to change the type of a column in a database view. Presumably, you are getting this error because you have a migration such as this (changing users.bio from string to text or something similar):

change_column :users, :bio, :text

When you run this migration, you will get an error because you have an existing view that uses this column. Let’s say that view name was awesome_users. To fix, you would update your migration like so:

drop_view :awesome_users
change_column :users, :bio, :text
create_view :awesome_users, version: whatever_the_version_number_you_last_used_was

If you re-run the migration now, it should succeed or it should give you a different view that is giving you problems. Rinse and repeat until the migration succeeds.

@leweafan

Понравилась статья? Поделить с друзьями:
  • Error cannot allocate memory errno 12
  • Error cannot allocate kernel buffer
  • Error cannot add user parameter
  • Error cannot add route connected route exists
  • Error cannot access servletexception