diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 1c3810e1a04..c0758b9244f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -30089,6 +30089,27 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
+
+
+
+ pg_tablespace_avail
+
+ pg_tablespace_avail ( name )
+ bigint
+
+
+ pg_tablespace_avail ( oid )
+ bigint
+
+
+ Returns the available disk space in the tablespace with the
+ specified name or OID. To use this function, you must
+ have CREATE privilege on the specified tablespace
+ or have privileges of the pg_read_all_stats role,
+ unless it is the default tablespace for the current database.
+
+
+
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index cedccc14129..9e1bec0b422 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1492,7 +1492,7 @@ SELECT $1 \parse stmt1
If x is appended to the command name, the results
are displayed in expanded mode.
If + is appended to the command name, each tablespace
- is listed with its associated options, on-disk size, permissions and
+ is listed with its associated options, on-disk size and free disk space, permissions and
description.
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 25865b660ef..a2637953ce0 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -12,6 +12,11 @@
#include "postgres.h"
#include
+#ifdef WIN32
+#include
+#else
+#include
+#endif
#include "access/htup_details.h"
#include "access/relation.h"
@@ -316,6 +321,99 @@ pg_tablespace_size_name(PG_FUNCTION_ARGS)
}
+/*
+ * Return available disk space of tablespace. Returns -1 if the tablespace
+ * directory cannot be found.
+ */
+static int64
+calculate_tablespace_avail(Oid tblspcOid)
+{
+ char tblspcPath[MAXPGPATH];
+ AclResult aclresult;
+#ifdef WIN32
+ ULARGE_INTEGER lpFreeBytesAvailable;
+#else
+ struct statvfs fst;
+#endif
+
+ /*
+ * User must have privileges of pg_read_all_stats or have CREATE privilege
+ * for target tablespace, either explicitly granted or implicitly because
+ * it is default for current database.
+ */
+ if (tblspcOid != MyDatabaseTableSpace &&
+ !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS))
+ {
+ aclresult = object_aclcheck(TableSpaceRelationId, tblspcOid, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_TABLESPACE,
+ get_tablespace_name(tblspcOid));
+ }
+
+ if (tblspcOid == DEFAULTTABLESPACE_OID)
+ snprintf(tblspcPath, MAXPGPATH, "base");
+ else if (tblspcOid == GLOBALTABLESPACE_OID)
+ snprintf(tblspcPath, MAXPGPATH, "global");
+ else
+ snprintf(tblspcPath, MAXPGPATH, "%s/%u/%s", PG_TBLSPC_DIR, tblspcOid,
+ TABLESPACE_VERSION_DIRECTORY);
+
+#ifdef WIN32
+ if (GetDiskFreeSpaceEx(tblspcPath, &lpFreeBytesAvailable, NULL, NULL) == false)
+ return -1;
+
+ return lpFreeBytesAvailable.QuadPart; /* ULONGLONG part of ULARGE_INTEGER */
+#else
+ if (statvfs(tblspcPath, &fst) < 0)
+ return -1;
+
+#if defined(__darwin__)
+ return fst.f_bavail * fst.f_frsize; /* available blocks times block size */
+#else
+ return fst.f_bavail * fst.f_bsize; /* available blocks times block size */
+#endif /* __darwin__ */
+#endif
+}
+
+Datum
+pg_tablespace_avail_oid(PG_FUNCTION_ARGS)
+{
+ Oid tblspcOid = PG_GETARG_OID(0);
+ int64 avail;
+
+ /*
+ * Not needed for correctness, but avoid non-user-facing error message
+ * later if the tablespace doesn't exist.
+ */
+ if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tblspcOid)))
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace with OID %u does not exist", tblspcOid));
+
+ avail = calculate_tablespace_avail(tblspcOid);
+
+ if (avail < 0)
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT64(avail);
+}
+
+Datum
+pg_tablespace_avail_name(PG_FUNCTION_ARGS)
+{
+ Name tblspcName = PG_GETARG_NAME(0);
+ Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
+ int64 avail;
+
+ avail = calculate_tablespace_avail(tblspcOid);
+
+ if (avail < 0)
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT64(avail);
+}
+
+
/*
* calculate size of (one fork of) a relation
*
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index e6cf468ac9e..8c52a126ac1 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -241,10 +241,15 @@ describeTablespaces(const char *pattern, bool verbose)
printACLColumn(&buf, "spcacl");
appendPQExpBuffer(&buf,
",\n spcoptions AS \"%s\""
- ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\""
- ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
+ ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\"",
gettext_noop("Options"),
- gettext_noop("Size"),
+ gettext_noop("Size"));
+ if (pset.sversion >= 180000)
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_avail(oid)) AS \"%s\"",
+ gettext_noop("Free"));
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
gettext_noop("Description"));
}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 890822eaf79..39c3b8c2552 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7683,6 +7683,14 @@
descr => 'total disk space usage for the specified tablespace',
proname => 'pg_tablespace_size', provolatile => 'v', prorettype => 'int8',
proargtypes => 'name', prosrc => 'pg_tablespace_size_name' },
+{ oid => '6015',
+ descr => 'disk stats for the specified tablespace',
+ proname => 'pg_tablespace_avail', provolatile => 'v', prorettype => 'int8',
+ proargtypes => 'oid', prosrc => 'pg_tablespace_avail_oid' },
+{ oid => '6016',
+ descr => 'disk stats for the specified tablespace',
+ proname => 'pg_tablespace_avail', provolatile => 'v', prorettype => 'int8',
+ proargtypes => 'name', prosrc => 'pg_tablespace_avail_name' },
{ oid => '2324', descr => 'total disk space usage for the specified database',
proname => 'pg_database_size', provolatile => 'v', prorettype => 'int8',
proargtypes => 'oid', prosrc => 'pg_database_size_oid' },