Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a93a8e8

Browse files
df7cbCommitfest Bot
authored and
Commitfest Bot
committed
Add pg_tablespace_avail() functions
This exposes the f_avail value from statvfs() on tablespace directories on the SQL level, allowing monitoring of free disk space from within the server. On windows, GetDiskFreeSpaceEx() is used. Permissions required match those from pg_tablespace_size(). In psql, include a new "Free" column in \db+ output. Add test coverage for pg_tablespace_avail() and the previously not covered pg_tablespace_size() function.
1 parent 29f7ce6 commit a93a8e8

File tree

7 files changed

+171
-4
lines changed

7 files changed

+171
-4
lines changed

doc/src/sgml/func.sgml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30003,6 +30003,27 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
3000330003
</para></entry>
3000430004
</row>
3000530005

30006+
<row>
30007+
<entry role="func_table_entry"><para role="func_signature">
30008+
<indexterm>
30009+
<primary>pg_tablespace_avail</primary>
30010+
</indexterm>
30011+
<function>pg_tablespace_avail</function> ( <type>name</type> )
30012+
<returnvalue>bigint</returnvalue>
30013+
</para>
30014+
<para role="func_signature">
30015+
<function>pg_tablespace_avail</function> ( <type>oid</type> )
30016+
<returnvalue>bigint</returnvalue>
30017+
</para>
30018+
<para>
30019+
Returns the available disk space in the tablespace with the
30020+
specified name or OID. To use this function, you must
30021+
have <literal>CREATE</literal> privilege on the specified tablespace
30022+
or have privileges of the <literal>pg_read_all_stats</literal> role,
30023+
unless it is the default tablespace for the current database.
30024+
</para></entry>
30025+
</row>
30026+
3000630027
<row>
3000730028
<entry role="func_table_entry"><para role="func_signature">
3000830029
<indexterm>

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1492,7 +1492,7 @@ SELECT $1 \parse stmt1
14921492
If <literal>x</literal> is appended to the command name, the results
14931493
are displayed in expanded mode.
14941494
If <literal>+</literal> is appended to the command name, each tablespace
1495-
is listed with its associated options, on-disk size, permissions and
1495+
is listed with its associated options, on-disk size and free disk space, permissions and
14961496
description.
14971497
</para>
14981498
</listitem>

src/backend/utils/adt/dbsize.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
#include "postgres.h"
1313

1414
#include <sys/stat.h>
15+
#ifdef WIN32
16+
#include <fileapi.h>
17+
#include <errhandlingapi.h>
18+
#else
19+
#include <sys/statvfs.h>
20+
#endif
1521

1622
#include "access/htup_details.h"
1723
#include "access/relation.h"
@@ -316,6 +322,102 @@ pg_tablespace_size_name(PG_FUNCTION_ARGS)
316322
}
317323

318324

325+
/*
326+
* Return available disk space of tablespace. Returns -1 if the tablespace
327+
* directory cannot be found.
328+
*/
329+
static int64
330+
calculate_tablespace_avail(Oid tblspcOid)
331+
{
332+
char tblspcPath[MAXPGPATH];
333+
AclResult aclresult;
334+
#ifdef WIN32
335+
ULARGE_INTEGER lpFreeBytesAvailable;
336+
#else
337+
struct statvfs fst;
338+
#endif
339+
340+
/*
341+
* User must have privileges of pg_read_all_stats or have CREATE privilege
342+
* for target tablespace, either explicitly granted or implicitly because
343+
* it is default for current database.
344+
*/
345+
if (tblspcOid != MyDatabaseTableSpace &&
346+
!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS))
347+
{
348+
aclresult = object_aclcheck(TableSpaceRelationId, tblspcOid, GetUserId(), ACL_CREATE);
349+
if (aclresult != ACLCHECK_OK)
350+
aclcheck_error(aclresult, OBJECT_TABLESPACE,
351+
get_tablespace_name(tblspcOid));
352+
}
353+
354+
if (tblspcOid == DEFAULTTABLESPACE_OID)
355+
snprintf(tblspcPath, MAXPGPATH, "base");
356+
else if (tblspcOid == GLOBALTABLESPACE_OID)
357+
snprintf(tblspcPath, MAXPGPATH, "global");
358+
else
359+
snprintf(tblspcPath, MAXPGPATH, "%s/%u/%s", PG_TBLSPC_DIR, tblspcOid,
360+
TABLESPACE_VERSION_DIRECTORY);
361+
362+
#ifdef WIN32
363+
if (! GetDiskFreeSpaceEx(tblspcPath, &lpFreeBytesAvailable, NULL, NULL))
364+
elog(ERROR, "GetDiskFreeSpaceEx failed: error code %lu", GetLastError());
365+
366+
return lpFreeBytesAvailable.QuadPart; /* ULONGLONG part of ULARGE_INTEGER */
367+
#else
368+
if (statvfs(tblspcPath, &fst) < 0)
369+
{
370+
if (errno == ENOENT)
371+
return -1;
372+
else
373+
ereport(ERROR,
374+
(errcode_for_file_access(),
375+
errmsg("could not statvfs directory \"%s\": %m", tblspcPath)));
376+
}
377+
378+
return fst.f_bavail * fst.f_frsize; /* available blocks times fragment size */
379+
#endif
380+
}
381+
382+
Datum
383+
pg_tablespace_avail_oid(PG_FUNCTION_ARGS)
384+
{
385+
Oid tblspcOid = PG_GETARG_OID(0);
386+
int64 avail;
387+
388+
/*
389+
* Not needed for correctness, but avoid non-user-facing error message
390+
* later if the tablespace doesn't exist.
391+
*/
392+
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tblspcOid)))
393+
ereport(ERROR,
394+
errcode(ERRCODE_UNDEFINED_OBJECT),
395+
errmsg("tablespace with OID %u does not exist", tblspcOid));
396+
397+
avail = calculate_tablespace_avail(tblspcOid);
398+
399+
if (avail < 0)
400+
PG_RETURN_NULL();
401+
402+
PG_RETURN_INT64(avail);
403+
}
404+
405+
Datum
406+
pg_tablespace_avail_name(PG_FUNCTION_ARGS)
407+
{
408+
Name tblspcName = PG_GETARG_NAME(0);
409+
Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
410+
int64 avail;
411+
412+
avail = calculate_tablespace_avail(tblspcOid);
413+
414+
if (avail < 0)
415+
PG_RETURN_NULL();
416+
417+
PG_RETURN_INT64(avail);
418+
}
419+
420+
319421
/*
320422
* calculate size of (one fork of) a relation
321423
*

src/bin/psql/describe.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,15 @@ describeTablespaces(const char *pattern, bool verbose)
241241
printACLColumn(&buf, "spcacl");
242242
appendPQExpBuffer(&buf,
243243
",\n spcoptions AS \"%s\""
244-
",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\""
245-
",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
244+
",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\"",
246245
gettext_noop("Options"),
247-
gettext_noop("Size"),
246+
gettext_noop("Size"));
247+
if (pset.sversion >= 180000)
248+
appendPQExpBuffer(&buf,
249+
",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_avail(oid)) AS \"%s\"",
250+
gettext_noop("Free"));
251+
appendPQExpBuffer(&buf,
252+
",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
248253
gettext_noop("Description"));
249254
}
250255

src/include/catalog/pg_proc.dat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7733,6 +7733,14 @@
77337733
descr => 'total disk space usage for the specified tablespace',
77347734
proname => 'pg_tablespace_size', provolatile => 'v', prorettype => 'int8',
77357735
proargtypes => 'name', prosrc => 'pg_tablespace_size_name' },
7736+
{ oid => '6015',
7737+
descr => 'disk stats for the specified tablespace',
7738+
proname => 'pg_tablespace_avail', provolatile => 'v', prorettype => 'int8',
7739+
proargtypes => 'oid', prosrc => 'pg_tablespace_avail_oid' },
7740+
{ oid => '6016',
7741+
descr => 'disk stats for the specified tablespace',
7742+
proname => 'pg_tablespace_avail', provolatile => 'v', prorettype => 'int8',
7743+
proargtypes => 'name', prosrc => 'pg_tablespace_avail_name' },
77367744
{ oid => '2324', descr => 'total disk space usage for the specified database',
77377745
proname => 'pg_database_size', provolatile => 'v', prorettype => 'int8',
77387746
proargtypes => 'oid', prosrc => 'pg_database_size_oid' },

src/test/regress/expected/tablespace.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'regress_tblspacewith';
2020
{random_page_cost=3.0}
2121
(1 row)
2222

23+
-- check size functions
24+
SELECT pg_tablespace_size('pg_default') BETWEEN 1_000_000 and 10_000_000_000, -- rough sanity check
25+
pg_tablespace_size('pg_global') BETWEEN 100_000 and 10_000_000,
26+
pg_tablespace_size('regress_tblspacewith'); -- empty
27+
?column? | ?column? | pg_tablespace_size
28+
----------+----------+--------------------
29+
t | t | 0
30+
(1 row)
31+
32+
SELECT pg_tablespace_size('missing');
33+
ERROR: tablespace "missing" does not exist
34+
SELECT pg_tablespace_avail('pg_default') > 1_000_000,
35+
pg_tablespace_avail('pg_global') > 1_000_000,
36+
pg_tablespace_avail('regress_tblspacewith') > 1_000_000;
37+
?column? | ?column? | ?column?
38+
----------+----------+----------
39+
t | t | t
40+
(1 row)
41+
42+
SELECT pg_tablespace_avail('missing');
43+
ERROR: tablespace "missing" does not exist
2344
-- drop the tablespace so we can re-use the location
2445
DROP TABLESPACE regress_tblspacewith;
2546
-- This returns a relative path as of an effect of allow_in_place_tablespaces,

src/test/regress/sql/tablespace.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ CREATE TABLESPACE regress_tblspacewith LOCATION '' WITH (random_page_cost = 3.0)
1717
-- check to see the parameter was used
1818
SELECT spcoptions FROM pg_tablespace WHERE spcname = 'regress_tblspacewith';
1919

20+
-- check size functions
21+
SELECT pg_tablespace_size('pg_default') BETWEEN 1_000_000 and 10_000_000_000, -- rough sanity check
22+
pg_tablespace_size('pg_global') BETWEEN 100_000 and 10_000_000,
23+
pg_tablespace_size('regress_tblspacewith'); -- empty
24+
SELECT pg_tablespace_size('missing');
25+
SELECT pg_tablespace_avail('pg_default') > 1_000_000,
26+
pg_tablespace_avail('pg_global') > 1_000_000,
27+
pg_tablespace_avail('regress_tblspacewith') > 1_000_000;
28+
SELECT pg_tablespace_avail('missing');
29+
2030
-- drop the tablespace so we can re-use the location
2131
DROP TABLESPACE regress_tblspacewith;
2232

0 commit comments

Comments
 (0)