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
asked Jul 8, 2020 at 11:04
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 $$;
answered Jul 8, 2020 at 11:28
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
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.
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?
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
.
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.
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.