Skip to content

Commit c6a6047

Browse files
committed
Prevent pgstats from getting confused when relkind of a relation changes
When the relkind of a relache entry changes, because a table is converted into a view, pgstats can get confused in 15+, leading to crashes or assertion failures. For HEAD, Tom fixed this in b23cd18, by removing support for converting a table to a view, removing the source of the inconsistency. This commit just adds an assertion that a relcache entry's relkind does not change, just in case we end up with another case of that in the future. As there's no cases of changing relkind anymore, we can't add a test that that's handled correctly. For 15, fix the problem by not maintaining the association with the old pgstat entry when the relkind changes during a relcache invalidation processing. In that case the pgstat entry needs to be unlinked first, to avoid PgStat_TableStatus->relation getting out of sync. Also add a test reproducing the issues. No known problem exists in 11-14, so just add the test there. Reported-by: vignesh C <vignesh21@gmail.com> Author: Andres Freund <andres@anarazel.de> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com Discussion: https://postgr.es/m/CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com Backpatch: 15-
1 parent 97299cf commit c6a6047

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

src/backend/utils/cache/relcache.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2626,6 +2626,7 @@ RelationClearRelation(Relation relation, bool rebuild)
26262626
bool keep_rules;
26272627
bool keep_policies;
26282628
bool keep_partkey;
2629+
bool keep_pgstats;
26292630

26302631
/* Build temporary entry, but don't link it into hashtable */
26312632
newrel = RelationBuildDesc(save_relid, false);
@@ -2667,6 +2668,21 @@ RelationClearRelation(Relation relation, bool rebuild)
26672668
/* partkey is immutable once set up, so we can always keep it */
26682669
keep_partkey = (relation->rd_partkey != NULL);
26692670

2671+
/*
2672+
* Keep stats pointers, except when the relkind changes (e.g. when
2673+
* converting tables into views). Different kinds of relations might
2674+
* have different types of stats.
2675+
*
2676+
* If we don't want to keep the stats, unlink the stats and relcache
2677+
* entry (and do so before entering the "critical section"
2678+
* below). This is important because otherwise
2679+
* PgStat_TableStatus->relation would get out of sync with
2680+
* relation->pgstat_info.
2681+
*/
2682+
keep_pgstats = relation->rd_rel->relkind == newrel->rd_rel->relkind;
2683+
if (!keep_pgstats)
2684+
pgstat_unlink_relation(relation);
2685+
26702686
/*
26712687
* Perform swapping of the relcache entry contents. Within this
26722688
* process the old entry is momentarily invalid, so there *must* be no
@@ -2720,9 +2736,14 @@ RelationClearRelation(Relation relation, bool rebuild)
27202736
SWAPFIELD(RowSecurityDesc *, rd_rsdesc);
27212737
/* toast OID override must be preserved */
27222738
SWAPFIELD(Oid, rd_toastoid);
2739+
27232740
/* pgstat_info / enabled must be preserved */
2724-
SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
2725-
SWAPFIELD(bool, pgstat_enabled);
2741+
if (keep_pgstats)
2742+
{
2743+
SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
2744+
SWAPFIELD(bool, pgstat_enabled);
2745+
}
2746+
27262747
/* preserve old partition key if we have one */
27272748
if (keep_partkey)
27282749
{

src/test/regress/expected/create_view.out

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,29 @@ select pg_get_viewdef('tt26v', true);
21222122
FROM ( VALUES (1,2,3)) v(x, y, z);
21232123
(1 row)
21242124

2125+
-- Test that changing the relkind of a relcache entry doesn't cause
2126+
-- trouble. Prior instances of where it did:
2127+
-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com
2128+
-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com
2129+
CREATE TABLE tt26(c int);
2130+
BEGIN;
2131+
CREATE TABLE tt27(c int);
2132+
SAVEPOINT q;
2133+
CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
2134+
SELECT * FROM tt27;
2135+
c
2136+
---
2137+
(0 rows)
2138+
2139+
ROLLBACK TO q;
2140+
CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
2141+
ROLLBACK;
2142+
BEGIN;
2143+
CREATE TABLE tt28(c int);
2144+
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
2145+
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
2146+
ERROR: "tt28" is already a view
2147+
ROLLBACK;
21252148
-- clean up all the random objects we made above
21262149
DROP SCHEMA temp_view_test CASCADE;
21272150
NOTICE: drop cascades to 27 other objects
@@ -2153,7 +2176,7 @@ drop cascades to view aliased_view_2
21532176
drop cascades to view aliased_view_3
21542177
drop cascades to view aliased_view_4
21552178
DROP SCHEMA testviewschm2 CASCADE;
2156-
NOTICE: drop cascades to 77 other objects
2179+
NOTICE: drop cascades to 78 other objects
21572180
DETAIL: drop cascades to table t1
21582181
drop cascades to view temporal1
21592182
drop cascades to view temporal2
@@ -2231,3 +2254,4 @@ drop cascades to view tt23v
22312254
drop cascades to view tt24v
22322255
drop cascades to view tt25v
22332256
drop cascades to view tt26v
2257+
drop cascades to table tt26

src/test/regress/sql/create_view.sql

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,29 @@ select x + y + z as c1,
781781
from (values(1,2,3)) v(x,y,z);
782782
select pg_get_viewdef('tt26v', true);
783783

784+
785+
-- Test that changing the relkind of a relcache entry doesn't cause
786+
-- trouble. Prior instances of where it did:
787+
-- CALDaNm2yXz+zOtv7y5zBd5WKT8O0Ld3YxikuU3dcyCvxF7gypA@mail.gmail.com
788+
-- CALDaNm3oZA-8Wbps2Jd1g5_Gjrr-x3YWrJPek-mF5Asrrvz2Dg@mail.gmail.com
789+
CREATE TABLE tt26(c int);
790+
791+
BEGIN;
792+
CREATE TABLE tt27(c int);
793+
SAVEPOINT q;
794+
CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
795+
SELECT * FROM tt27;
796+
ROLLBACK TO q;
797+
CREATE RULE "_RETURN" AS ON SELECT TO tt27 DO INSTEAD SELECT * FROM tt26;
798+
ROLLBACK;
799+
800+
BEGIN;
801+
CREATE TABLE tt28(c int);
802+
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
803+
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
804+
ROLLBACK;
805+
806+
784807
-- clean up all the random objects we made above
785808
DROP SCHEMA temp_view_test CASCADE;
786809
DROP SCHEMA testviewschm2 CASCADE;

0 commit comments

Comments
 (0)