Skip to content

Commit f3dae2a

Browse files
committed
Do not use in-place updates for statistics import.
The use of in-place updates was originally there to follow the precedent of ANALYZE and to reduce the potential for bloat on pg_class. Per discussion, it's not worth the risks. Reported-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/cpdanvzykcb5o64rmapkx6n5gjypoce3y52hff7ocxupgpbxu4@53jmlyvukijo
1 parent 3ce3575 commit f3dae2a

File tree

3 files changed

+47
-177
lines changed

3 files changed

+47
-177
lines changed

src/backend/statistics/relation_stats.c

Lines changed: 47 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,13 @@ static struct StatsArgInfo relarginfo[] =
4848
[NUM_RELATION_STATS_ARGS] = {0}
4949
};
5050

51-
static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel,
52-
bool inplace);
51+
static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel);
5352

5453
/*
5554
* Internal function for modifying statistics for a relation.
5655
*/
5756
static bool
58-
relation_statistics_update(FunctionCallInfo fcinfo, int elevel, bool inplace)
57+
relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
5958
{
6059
bool result = true;
6160
Oid reloid;
@@ -66,6 +65,12 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel, bool inplace)
6665
bool update_reltuples = false;
6766
BlockNumber relallvisible = 0;
6867
bool update_relallvisible = false;
68+
HeapTuple ctup;
69+
Form_pg_class pgcform;
70+
int replaces[3] = {0};
71+
Datum values[3] = {0};
72+
bool nulls[3] = {0};
73+
int nreplaces = 0;
6974

7075
if (!PG_ARGISNULL(RELPAGES_ARG))
7176
{
@@ -110,101 +115,52 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel, bool inplace)
110115
*/
111116
crel = table_open(RelationRelationId, RowExclusiveLock);
112117

113-
if (inplace)
118+
ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
119+
if (!HeapTupleIsValid(ctup))
114120
{
115-
HeapTuple ctup = NULL;
116-
ScanKeyData key[1];
117-
Form_pg_class pgcform;
118-
void *inplace_state = NULL;
119-
bool dirty = false;
120-
121-
ScanKeyInit(&key[0], Anum_pg_class_oid, BTEqualStrategyNumber, F_OIDEQ,
122-
ObjectIdGetDatum(reloid));
123-
systable_inplace_update_begin(crel, ClassOidIndexId, true, NULL, 1, key,
124-
&ctup, &inplace_state);
125-
if (!HeapTupleIsValid(ctup))
126-
elog(ERROR, "pg_class entry for relid %u vanished while updating statistics",
127-
reloid);
128-
pgcform = (Form_pg_class) GETSTRUCT(ctup);
129-
130-
if (update_relpages && pgcform->relpages != relpages)
131-
{
132-
pgcform->relpages = relpages;
133-
dirty = true;
134-
}
135-
if (update_reltuples && pgcform->reltuples != reltuples)
136-
{
137-
pgcform->reltuples = reltuples;
138-
dirty = true;
139-
}
140-
if (update_relallvisible && pgcform->relallvisible != relallvisible)
141-
{
142-
pgcform->relallvisible = relallvisible;
143-
dirty = true;
144-
}
145-
146-
if (dirty)
147-
systable_inplace_update_finish(inplace_state, ctup);
148-
else
149-
systable_inplace_update_cancel(inplace_state);
150-
151-
heap_freetuple(ctup);
121+
ereport(elevel,
122+
(errcode(ERRCODE_OBJECT_IN_USE),
123+
errmsg("pg_class entry for relid %u not found", reloid)));
124+
table_close(crel, RowExclusiveLock);
125+
return false;
152126
}
153-
else
154-
{
155-
TupleDesc tupdesc = RelationGetDescr(crel);
156-
HeapTuple ctup;
157-
Form_pg_class pgcform;
158-
int replaces[3] = {0};
159-
Datum values[3] = {0};
160-
bool nulls[3] = {0};
161-
int nreplaces = 0;
162-
163-
ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
164-
if (!HeapTupleIsValid(ctup))
165-
{
166-
ereport(elevel,
167-
(errcode(ERRCODE_OBJECT_IN_USE),
168-
errmsg("pg_class entry for relid %u not found", reloid)));
169-
table_close(crel, RowExclusiveLock);
170-
return false;
171-
}
172-
pgcform = (Form_pg_class) GETSTRUCT(ctup);
173127

174-
if (update_relpages && relpages != pgcform->relpages)
175-
{
176-
replaces[nreplaces] = Anum_pg_class_relpages;
177-
values[nreplaces] = UInt32GetDatum(relpages);
178-
nreplaces++;
179-
}
128+
pgcform = (Form_pg_class) GETSTRUCT(ctup);
180129

181-
if (update_reltuples && reltuples != pgcform->reltuples)
182-
{
183-
replaces[nreplaces] = Anum_pg_class_reltuples;
184-
values[nreplaces] = Float4GetDatum(reltuples);
185-
nreplaces++;
186-
}
130+
if (update_relpages && relpages != pgcform->relpages)
131+
{
132+
replaces[nreplaces] = Anum_pg_class_relpages;
133+
values[nreplaces] = UInt32GetDatum(relpages);
134+
nreplaces++;
135+
}
187136

188-
if (update_relallvisible && relallvisible != pgcform->relallvisible)
189-
{
190-
replaces[nreplaces] = Anum_pg_class_relallvisible;
191-
values[nreplaces] = UInt32GetDatum(relallvisible);
192-
nreplaces++;
193-
}
137+
if (update_reltuples && reltuples != pgcform->reltuples)
138+
{
139+
replaces[nreplaces] = Anum_pg_class_reltuples;
140+
values[nreplaces] = Float4GetDatum(reltuples);
141+
nreplaces++;
142+
}
194143

195-
if (nreplaces > 0)
196-
{
197-
HeapTuple newtup;
144+
if (update_relallvisible && relallvisible != pgcform->relallvisible)
145+
{
146+
replaces[nreplaces] = Anum_pg_class_relallvisible;
147+
values[nreplaces] = UInt32GetDatum(relallvisible);
148+
nreplaces++;
149+
}
198150

199-
newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
200-
replaces, values, nulls);
201-
CatalogTupleUpdate(crel, &newtup->t_self, newtup);
202-
heap_freetuple(newtup);
203-
}
151+
if (nreplaces > 0)
152+
{
153+
TupleDesc tupdesc = RelationGetDescr(crel);
154+
HeapTuple newtup;
204155

205-
ReleaseSysCache(ctup);
156+
newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
157+
replaces, values, nulls);
158+
CatalogTupleUpdate(crel, &newtup->t_self, newtup);
159+
heap_freetuple(newtup);
206160
}
207161

162+
ReleaseSysCache(ctup);
163+
208164
/* release the lock, consistent with vac_update_relstats() */
209165
table_close(crel, RowExclusiveLock);
210166

@@ -219,7 +175,7 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel, bool inplace)
219175
Datum
220176
pg_set_relation_stats(PG_FUNCTION_ARGS)
221177
{
222-
relation_statistics_update(fcinfo, ERROR, false);
178+
relation_statistics_update(fcinfo, ERROR);
223179
PG_RETURN_VOID();
224180
}
225181

@@ -243,7 +199,7 @@ pg_clear_relation_stats(PG_FUNCTION_ARGS)
243199
newfcinfo->args[3].value = UInt32GetDatum(0);
244200
newfcinfo->args[3].isnull = false;
245201

246-
relation_statistics_update(newfcinfo, ERROR, false);
202+
relation_statistics_update(newfcinfo, ERROR);
247203
PG_RETURN_VOID();
248204
}
249205

@@ -261,7 +217,7 @@ pg_restore_relation_stats(PG_FUNCTION_ARGS)
261217
relarginfo, WARNING))
262218
result = false;
263219

264-
if (!relation_statistics_update(positional_fcinfo, WARNING, true))
220+
if (!relation_statistics_update(positional_fcinfo, WARNING))
265221
result = false;
266222

267223
PG_RETURN_BOOL(result);

src/test/regress/expected/stats_import.out

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -143,39 +143,6 @@ WHERE oid = 'stats_import.test'::regclass;
143143
18 | 401 | 5
144144
(1 row)
145145

146-
-- test MVCC behavior: changes do not persist after abort (in contrast
147-
-- to pg_restore_relation_stats(), which uses in-place updates).
148-
BEGIN;
149-
SELECT
150-
pg_catalog.pg_set_relation_stats(
151-
relation => 'stats_import.test'::regclass,
152-
relpages => NULL::integer,
153-
reltuples => 4000.0::real,
154-
relallvisible => 4::integer);
155-
pg_set_relation_stats
156-
-----------------------
157-
158-
(1 row)
159-
160-
ABORT;
161-
SELECT relpages, reltuples, relallvisible
162-
FROM pg_class
163-
WHERE oid = 'stats_import.test'::regclass;
164-
relpages | reltuples | relallvisible
165-
----------+-----------+---------------
166-
18 | 401 | 5
167-
(1 row)
168-
169-
BEGIN;
170-
SELECT
171-
pg_catalog.pg_clear_relation_stats(
172-
'stats_import.test'::regclass);
173-
pg_clear_relation_stats
174-
-------------------------
175-
176-
(1 row)
177-
178-
ABORT;
179146
SELECT relpages, reltuples, relallvisible
180147
FROM pg_class
181148
WHERE oid = 'stats_import.test'::regclass;
@@ -836,25 +803,6 @@ WHERE oid = 'stats_import.test'::regclass;
836803
(1 row)
837804

838805
-- ok: just relpages
839-
SELECT pg_restore_relation_stats(
840-
'relation', 'stats_import.test'::regclass,
841-
'version', 150000::integer,
842-
'relpages', '15'::integer);
843-
pg_restore_relation_stats
844-
---------------------------
845-
t
846-
(1 row)
847-
848-
SELECT relpages, reltuples, relallvisible
849-
FROM pg_class
850-
WHERE oid = 'stats_import.test'::regclass;
851-
relpages | reltuples | relallvisible
852-
----------+-----------+---------------
853-
15 | 400 | 4
854-
(1 row)
855-
856-
-- test non-MVCC behavior: new value should persist after abort
857-
BEGIN;
858806
SELECT pg_restore_relation_stats(
859807
'relation', 'stats_import.test'::regclass,
860808
'version', 150000::integer,
@@ -864,7 +812,6 @@ SELECT pg_restore_relation_stats(
864812
t
865813
(1 row)
866814

867-
ABORT;
868815
SELECT relpages, reltuples, relallvisible
869816
FROM pg_class
870817
WHERE oid = 'stats_import.test'::regclass;

src/test/regress/sql/stats_import.sql

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -100,27 +100,6 @@ SELECT relpages, reltuples, relallvisible
100100
FROM pg_class
101101
WHERE oid = 'stats_import.test'::regclass;
102102

103-
-- test MVCC behavior: changes do not persist after abort (in contrast
104-
-- to pg_restore_relation_stats(), which uses in-place updates).
105-
BEGIN;
106-
SELECT
107-
pg_catalog.pg_set_relation_stats(
108-
relation => 'stats_import.test'::regclass,
109-
relpages => NULL::integer,
110-
reltuples => 4000.0::real,
111-
relallvisible => 4::integer);
112-
ABORT;
113-
114-
SELECT relpages, reltuples, relallvisible
115-
FROM pg_class
116-
WHERE oid = 'stats_import.test'::regclass;
117-
118-
BEGIN;
119-
SELECT
120-
pg_catalog.pg_clear_relation_stats(
121-
'stats_import.test'::regclass);
122-
ABORT;
123-
124103
SELECT relpages, reltuples, relallvisible
125104
FROM pg_class
126105
WHERE oid = 'stats_import.test'::regclass;
@@ -649,22 +628,10 @@ FROM pg_class
649628
WHERE oid = 'stats_import.test'::regclass;
650629

651630
-- ok: just relpages
652-
SELECT pg_restore_relation_stats(
653-
'relation', 'stats_import.test'::regclass,
654-
'version', 150000::integer,
655-
'relpages', '15'::integer);
656-
657-
SELECT relpages, reltuples, relallvisible
658-
FROM pg_class
659-
WHERE oid = 'stats_import.test'::regclass;
660-
661-
-- test non-MVCC behavior: new value should persist after abort
662-
BEGIN;
663631
SELECT pg_restore_relation_stats(
664632
'relation', 'stats_import.test'::regclass,
665633
'version', 150000::integer,
666634
'relpages', '16'::integer);
667-
ABORT;
668635

669636
SELECT relpages, reltuples, relallvisible
670637
FROM pg_class

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy