Skip to content

Commit fa352d6

Browse files
committed
Make pg_relation_size() and friends return NULL if the object doesn't exist.
That avoids errors when the functions are used in queries like "SELECT pg_relation_size(oid) FROM pg_class", and a table is dropped concurrently. Phil Sorber
1 parent 6f6b46c commit fa352d6

File tree

2 files changed

+89
-35
lines changed

2 files changed

+89
-35
lines changed

doc/src/sgml/func.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14979,6 +14979,11 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
1497914979
the table name.
1498014980
</para>
1498114981

14982+
<para>
14983+
If an OID that does not represent an existing object is passed as
14984+
argument to one of the above functions, NULL is returned.
14985+
</para>
14986+
1498214987
<para>
1498314988
The functions shown in <xref linkend="functions-admin-dblocation"> assist
1498414989
in identifying the specific disk files associated with database objects.

src/backend/utils/adt/dbsize.c

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -120,35 +120,42 @@ calculate_database_size(Oid dbOid)
120120

121121
FreeDir(dirdesc);
122122

123-
/* Complain if we found no trace of the DB at all */
124-
if (!totalsize)
125-
ereport(ERROR,
126-
(ERRCODE_UNDEFINED_DATABASE,
127-
errmsg("database with OID %u does not exist", dbOid)));
128-
129123
return totalsize;
130124
}
131125

132126
Datum
133127
pg_database_size_oid(PG_FUNCTION_ARGS)
134128
{
135129
Oid dbOid = PG_GETARG_OID(0);
130+
int64 size;
136131

137-
PG_RETURN_INT64(calculate_database_size(dbOid));
132+
size = calculate_database_size(dbOid);
133+
134+
if (size == 0)
135+
PG_RETURN_NULL();
136+
137+
PG_RETURN_INT64(size);
138138
}
139139

140140
Datum
141141
pg_database_size_name(PG_FUNCTION_ARGS)
142142
{
143143
Name dbName = PG_GETARG_NAME(0);
144144
Oid dbOid = get_database_oid(NameStr(*dbName), false);
145+
int64 size;
146+
147+
size = calculate_database_size(dbOid);
145148

146-
PG_RETURN_INT64(calculate_database_size(dbOid));
149+
if (size == 0)
150+
PG_RETURN_NULL();
151+
152+
PG_RETURN_INT64(size);
147153
}
148154

149155

150156
/*
151-
* calculate total size of tablespace
157+
* Calculate total size of tablespace. Returns -1 if the tablespace directory
158+
* cannot be found.
152159
*/
153160
static int64
154161
calculate_tablespace_size(Oid tblspcOid)
@@ -184,10 +191,7 @@ calculate_tablespace_size(Oid tblspcOid)
184191
dirdesc = AllocateDir(tblspcPath);
185192

186193
if (!dirdesc)
187-
ereport(ERROR,
188-
(errcode_for_file_access(),
189-
errmsg("could not open tablespace directory \"%s\": %m",
190-
tblspcPath)));
194+
return -1;
191195

192196
while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
193197
{
@@ -226,17 +230,29 @@ Datum
226230
pg_tablespace_size_oid(PG_FUNCTION_ARGS)
227231
{
228232
Oid tblspcOid = PG_GETARG_OID(0);
233+
int64 size;
234+
235+
size = calculate_tablespace_size(tblspcOid);
229236

230-
PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
237+
if (size < 0)
238+
PG_RETURN_NULL();
239+
240+
PG_RETURN_INT64(size);
231241
}
232242

233243
Datum
234244
pg_tablespace_size_name(PG_FUNCTION_ARGS)
235245
{
236246
Name tblspcName = PG_GETARG_NAME(0);
237247
Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
248+
int64 size;
238249

239-
PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
250+
size = calculate_tablespace_size(tblspcOid);
251+
252+
if (size < 0)
253+
PG_RETURN_NULL();
254+
255+
PG_RETURN_INT64(size);
240256
}
241257

242258

@@ -289,7 +305,17 @@ pg_relation_size(PG_FUNCTION_ARGS)
289305
Relation rel;
290306
int64 size;
291307

292-
rel = relation_open(relOid, AccessShareLock);
308+
rel = try_relation_open(relOid, AccessShareLock);
309+
310+
/*
311+
* Before 9.2, we used to throw an error if the relation didn't exist, but
312+
* that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
313+
* less robust, because while we scan pg_class with an MVCC snapshot,
314+
* someone else might drop the table. It's better to return NULL for
315+
* alread-dropped tables than throw an error and abort the whole query.
316+
*/
317+
if (rel == NULL)
318+
PG_RETURN_NULL();
293319

294320
size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
295321
forkname_to_number(text_to_cstring(forkName)));
@@ -339,14 +365,11 @@ calculate_toast_table_size(Oid toastrelid)
339365
* those won't have attached toast tables, but they can have multiple forks.
340366
*/
341367
static int64
342-
calculate_table_size(Oid relOid)
368+
calculate_table_size(Relation rel)
343369
{
344370
int64 size = 0;
345-
Relation rel;
346371
ForkNumber forkNum;
347372

348-
rel = relation_open(relOid, AccessShareLock);
349-
350373
/*
351374
* heap size, including FSM and VM
352375
*/
@@ -360,8 +383,6 @@ calculate_table_size(Oid relOid)
360383
if (OidIsValid(rel->rd_rel->reltoastrelid))
361384
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
362385

363-
relation_close(rel, AccessShareLock);
364-
365386
return size;
366387
}
367388

@@ -371,12 +392,9 @@ calculate_table_size(Oid relOid)
371392
* Can be applied safely to an index, but you'll just get zero.
372393
*/
373394
static int64
374-
calculate_indexes_size(Oid relOid)
395+
calculate_indexes_size(Relation rel)
375396
{
376397
int64 size = 0;
377-
Relation rel;
378-
379-
rel = relation_open(relOid, AccessShareLock);
380398

381399
/*
382400
* Aggregate all indexes on the given relation
@@ -405,56 +423,87 @@ calculate_indexes_size(Oid relOid)
405423
list_free(index_oids);
406424
}
407425

408-
relation_close(rel, AccessShareLock);
409-
410426
return size;
411427
}
412428

413429
Datum
414430
pg_table_size(PG_FUNCTION_ARGS)
415431
{
416432
Oid relOid = PG_GETARG_OID(0);
433+
Relation rel;
434+
int64 size;
435+
436+
rel = try_relation_open(relOid, AccessShareLock);
437+
438+
if (rel == NULL)
439+
PG_RETURN_NULL();
417440

418-
PG_RETURN_INT64(calculate_table_size(relOid));
441+
size = calculate_table_size(rel);
442+
443+
relation_close(rel, AccessShareLock);
444+
445+
PG_RETURN_INT64(size);
419446
}
420447

421448
Datum
422449
pg_indexes_size(PG_FUNCTION_ARGS)
423450
{
424451
Oid relOid = PG_GETARG_OID(0);
452+
Relation rel;
453+
int64 size;
425454

426-
PG_RETURN_INT64(calculate_indexes_size(relOid));
455+
rel = try_relation_open(relOid, AccessShareLock);
456+
457+
if (rel == NULL)
458+
PG_RETURN_NULL();
459+
460+
size = calculate_indexes_size(rel);
461+
462+
relation_close(rel, AccessShareLock);
463+
464+
PG_RETURN_INT64(size);
427465
}
428466

429467
/*
430468
* Compute the on-disk size of all files for the relation,
431469
* including heap data, index data, toast data, FSM, VM.
432470
*/
433471
static int64
434-
calculate_total_relation_size(Oid Relid)
472+
calculate_total_relation_size(Relation rel)
435473
{
436474
int64 size;
437475

438476
/*
439477
* Aggregate the table size, this includes size of the heap, toast and
440478
* toast index with free space and visibility map
441479
*/
442-
size = calculate_table_size(Relid);
480+
size = calculate_table_size(rel);
443481

444482
/*
445483
* Add size of all attached indexes as well
446484
*/
447-
size += calculate_indexes_size(Relid);
485+
size += calculate_indexes_size(rel);
448486

449487
return size;
450488
}
451489

452490
Datum
453491
pg_total_relation_size(PG_FUNCTION_ARGS)
454492
{
455-
Oid relid = PG_GETARG_OID(0);
493+
Oid relOid = PG_GETARG_OID(0);
494+
Relation rel;
495+
int64 size;
496+
497+
rel = try_relation_open(relOid, AccessShareLock);
498+
499+
if (rel == NULL)
500+
PG_RETURN_NULL();
456501

457-
PG_RETURN_INT64(calculate_total_relation_size(relid));
502+
size = calculate_total_relation_size(rel);
503+
504+
relation_close(rel, AccessShareLock);
505+
506+
PG_RETURN_INT64(size);
458507
}
459508

460509
/*

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