Reputation: 350
Suppose I have this foo table in a PostgreSQL 9.1 database:
CREATE TABLE foo
(
bar integer,
flg_deleted boolean
);
Along with this vwfoo view:
CREATE VIEW vwfoo AS
SELECT bar
FROM foo
WHERE flg_deleted = false;
Also assume I have an application running a couple of short-lived transactions per second, using vwfoo.
Now, I want to add a column baz to foo and I want baz to be in vwfoo too. But, of course, I don't want my application to get any errors because of those changes.
If I execute the following steps (in a single transaction) to perform the desired change:
Do I get the desired behavior (no errors in the application)?
Will there be an exclusive lock being held on vwfoo during the whole transaction (that's what I want)?
Is it possible that any transaction will try to use the view between steps 1 and 3 and then fail (instead of just blocking, waiting for a lock)?
Does the "identity" of vwfoo change when it's recreated? In other words: is it possible that any transaction will try to use the view between steps 1 and 3, block, resume after step 3 and then fail because the view was recreated?
Thanks.
Upvotes: 2
Views: 156
Reputation: 154454
A quick test suggests that this will cause problems with your application. To recreate, create two connections (A
and B
), then run:
A: BEGIN;
A: DROP VIEW vwfoo;
B: SELECT * FROM vwfoo;
(B blocks… )
A: CREATE VIEW vwfoo AS SELECT * FROM foo;
A: COMMIT;
(B yields:
ERROR: could not open relation with OID 326418
LINE 1: SELECT * FROM vwfoo
)
Instead, you should do this kind of atomic swapping by renaming the views:
A: CREATE VIEW vwfoo_new AS SELECT * FROM foo;
A: BEGIN;
A: ALTER VIEW vwfoo RENAME TO vwfoo_old;
B: SELECT * FROM vwfoo;
(B blocks…)
A: ALTER VIEW vwfoo_new RENAME TO vwfoo;
A: COMMIT;
(B completes as expected)
A: DROP TABLE vwfoo_old;
This will work as expected (and you won't need to run a (comparatively) expensive DROP TABLE
inside the transaction!)
Edit: You can use the same strategy to solve your "real" problem, too:
ALTER TABLE foo ADD COLUMN bar_new TEXT;
UPDATE foo SET bar_new = bar;
CREATE VIEW vwfoo_new AS SELECT bar_new AS bar FROM foo;
… do the view switcheroo …
DROP VIEW vwfoo_old;
BEGIN;
ALTER TABLE foo RENAME bar TO bar_old;
ALTER TABLE foo RENAME bar_new TO bar;
COMMIT;
ALTER TABLE foo DROP COLUMN bar_old;
The view referencing bar_new
will be correctly updated, too:
# \d vwfoo
View definition:
SELECT foo.a
FROM foo
WHERE foo.b = false;
# ALTER foo RENAME a TO new_a;
ALTER TABLE
# \d vwfoo
View definition:
SELECT foo.newa AS a
FROM foo
WHERE foo.b = false;
Upvotes: 1