Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)