3 * object size functions
5 * Copyright (c) 2002-2008, PostgreSQL Global Development Group
14 #include <sys/types.h>
17 #include "access/heapam.h"
18 #include "catalog/catalog.h"
19 #include "catalog/namespace.h"
20 #include "catalog/pg_tablespace.h"
21 #include "commands/dbcommands.h"
22 #include "commands/tablespace.h"
23 #include "miscadmin.h"
24 #include "storage/fd.h"
25 #include "utils/acl.h"
26 #include "utils/builtins.h"
27 #include "utils/rel.h"
28 #include "utils/syscache.h"
31 /* Return physical size of directory contents, or 0 if dir doesn't exist */
33 db_dir_size(const char *path
)
36 struct dirent
*direntry
;
38 char filename
[MAXPGPATH
];
40 dirdesc
= AllocateDir(path
);
45 while ((direntry
= ReadDir(dirdesc
, path
)) != NULL
)
49 if (strcmp(direntry
->d_name
, ".") == 0 ||
50 strcmp(direntry
->d_name
, "..") == 0)
53 snprintf(filename
, MAXPGPATH
, "%s/%s", path
, direntry
->d_name
);
55 if (stat(filename
, &fst
) < 0)
61 (errcode_for_file_access(),
62 errmsg("could not stat file \"%s\": %m", filename
)));
64 dirsize
+= fst
.st_size
;
72 * calculate size of database in all tablespaces
75 calculate_database_size(Oid dbOid
)
79 struct dirent
*direntry
;
80 char dirpath
[MAXPGPATH
];
81 char pathname
[MAXPGPATH
];
84 /* User must have connect privilege for target database */
85 aclresult
= pg_database_aclcheck(dbOid
, GetUserId(), ACL_CONNECT
);
86 if (aclresult
!= ACLCHECK_OK
)
87 aclcheck_error(aclresult
, ACL_KIND_DATABASE
,
88 get_database_name(dbOid
));
90 /* Shared storage in pg_global is not counted */
92 /* Include pg_default storage */
93 snprintf(pathname
, MAXPGPATH
, "base/%u", dbOid
);
94 totalsize
= db_dir_size(pathname
);
96 /* Scan the non-default tablespaces */
97 snprintf(dirpath
, MAXPGPATH
, "pg_tblspc");
98 dirdesc
= AllocateDir(dirpath
);
101 (errcode_for_file_access(),
102 errmsg("could not open tablespace directory \"%s\": %m",
105 while ((direntry
= ReadDir(dirdesc
, dirpath
)) != NULL
)
107 if (strcmp(direntry
->d_name
, ".") == 0 ||
108 strcmp(direntry
->d_name
, "..") == 0)
111 snprintf(pathname
, MAXPGPATH
, "pg_tblspc/%s/%u",
112 direntry
->d_name
, dbOid
);
113 totalsize
+= db_dir_size(pathname
);
118 /* Complain if we found no trace of the DB at all */
121 (ERRCODE_UNDEFINED_DATABASE
,
122 errmsg("database with OID %u does not exist", dbOid
)));
128 pg_database_size_oid(PG_FUNCTION_ARGS
)
130 Oid dbOid
= PG_GETARG_OID(0);
132 PG_RETURN_INT64(calculate_database_size(dbOid
));
136 pg_database_size_name(PG_FUNCTION_ARGS
)
138 Name dbName
= PG_GETARG_NAME(0);
139 Oid dbOid
= get_database_oid(NameStr(*dbName
));
141 if (!OidIsValid(dbOid
))
143 (errcode(ERRCODE_UNDEFINED_DATABASE
),
144 errmsg("database \"%s\" does not exist",
147 PG_RETURN_INT64(calculate_database_size(dbOid
));
152 * calculate total size of tablespace
155 calculate_tablespace_size(Oid tblspcOid
)
157 char tblspcPath
[MAXPGPATH
];
158 char pathname
[MAXPGPATH
];
161 struct dirent
*direntry
;
165 * User must have CREATE privilege for target tablespace, either
166 * explicitly granted or implicitly because it is default for current
169 if (tblspcOid
!= MyDatabaseTableSpace
)
171 aclresult
= pg_tablespace_aclcheck(tblspcOid
, GetUserId(), ACL_CREATE
);
172 if (aclresult
!= ACLCHECK_OK
)
173 aclcheck_error(aclresult
, ACL_KIND_TABLESPACE
,
174 get_tablespace_name(tblspcOid
));
177 if (tblspcOid
== DEFAULTTABLESPACE_OID
)
178 snprintf(tblspcPath
, MAXPGPATH
, "base");
179 else if (tblspcOid
== GLOBALTABLESPACE_OID
)
180 snprintf(tblspcPath
, MAXPGPATH
, "global");
182 snprintf(tblspcPath
, MAXPGPATH
, "pg_tblspc/%u", tblspcOid
);
184 dirdesc
= AllocateDir(tblspcPath
);
188 (errcode_for_file_access(),
189 errmsg("could not open tablespace directory \"%s\": %m",
192 while ((direntry
= ReadDir(dirdesc
, tblspcPath
)) != NULL
)
196 if (strcmp(direntry
->d_name
, ".") == 0 ||
197 strcmp(direntry
->d_name
, "..") == 0)
200 snprintf(pathname
, MAXPGPATH
, "%s/%s", tblspcPath
, direntry
->d_name
);
202 if (stat(pathname
, &fst
) < 0)
208 (errcode_for_file_access(),
209 errmsg("could not stat file \"%s\": %m", pathname
)));
212 if (S_ISDIR(fst
.st_mode
))
213 totalsize
+= db_dir_size(pathname
);
215 totalsize
+= fst
.st_size
;
224 pg_tablespace_size_oid(PG_FUNCTION_ARGS
)
226 Oid tblspcOid
= PG_GETARG_OID(0);
228 PG_RETURN_INT64(calculate_tablespace_size(tblspcOid
));
232 pg_tablespace_size_name(PG_FUNCTION_ARGS
)
234 Name tblspcName
= PG_GETARG_NAME(0);
235 Oid tblspcOid
= get_tablespace_oid(NameStr(*tblspcName
));
237 if (!OidIsValid(tblspcOid
))
239 (errcode(ERRCODE_UNDEFINED_OBJECT
),
240 errmsg("tablespace \"%s\" does not exist",
241 NameStr(*tblspcName
))));
243 PG_RETURN_INT64(calculate_tablespace_size(tblspcOid
));
248 * calculate size of a relation
251 calculate_relation_size(RelFileNode
*rfn
, ForkNumber forknum
)
255 char pathname
[MAXPGPATH
];
256 unsigned int segcount
= 0;
258 relationpath
= relpath(*rfn
, forknum
);
260 for (segcount
= 0;; segcount
++)
265 snprintf(pathname
, MAXPGPATH
, "%s",
268 snprintf(pathname
, MAXPGPATH
, "%s.%u",
269 relationpath
, segcount
);
271 if (stat(pathname
, &fst
) < 0)
277 (errcode_for_file_access(),
278 errmsg("could not stat file \"%s\": %m", pathname
)));
280 totalsize
+= fst
.st_size
;
287 pg_relation_size(PG_FUNCTION_ARGS
)
289 Oid relOid
= PG_GETARG_OID(0);
290 text
*forkName
= PG_GETARG_TEXT_P(1);
294 rel
= relation_open(relOid
, AccessShareLock
);
296 size
= calculate_relation_size(&(rel
->rd_node
),
297 forkname_to_number(text_to_cstring(forkName
)));
299 relation_close(rel
, AccessShareLock
);
301 PG_RETURN_INT64(size
);
306 * Compute the on-disk size of files for the relation according to the
307 * stat function, including heap data, index data, and toast data.
310 calculate_total_relation_size(Oid Relid
)
318 heapRel
= relation_open(Relid
, AccessShareLock
);
319 toastOid
= heapRel
->rd_rel
->reltoastrelid
;
321 /* Get the heap size */
323 for (forkNum
= 0; forkNum
<= MAX_FORKNUM
; forkNum
++)
324 size
+= calculate_relation_size(&(heapRel
->rd_node
), forkNum
);
326 /* Include any dependent indexes */
327 if (heapRel
->rd_rel
->relhasindex
)
329 List
*index_oids
= RelationGetIndexList(heapRel
);
331 foreach(cell
, index_oids
)
333 Oid idxOid
= lfirst_oid(cell
);
336 iRel
= relation_open(idxOid
, AccessShareLock
);
338 for (forkNum
= 0; forkNum
<= MAX_FORKNUM
; forkNum
++)
339 size
+= calculate_relation_size(&(iRel
->rd_node
), forkNum
);
341 relation_close(iRel
, AccessShareLock
);
344 list_free(index_oids
);
347 /* Recursively include toast table (and index) size */
348 if (OidIsValid(toastOid
))
349 size
+= calculate_total_relation_size(toastOid
);
351 relation_close(heapRel
, AccessShareLock
);
357 pg_total_relation_size(PG_FUNCTION_ARGS
)
359 Oid relid
= PG_GETARG_OID(0);
361 PG_RETURN_INT64(calculate_total_relation_size(relid
));
365 * formatting with size units
368 pg_size_pretty(PG_FUNCTION_ARGS
)
370 int64 size
= PG_GETARG_INT64(0);
372 int64 limit
= 10 * 1024;
375 if (size
< limit
* mult
)
376 snprintf(buf
, sizeof(buf
), INT64_FORMAT
" bytes", size
);
380 if (size
< limit
* mult
)
381 snprintf(buf
, sizeof(buf
), INT64_FORMAT
" kB",
382 (size
+ mult
/ 2) / mult
);
386 if (size
< limit
* mult
)
387 snprintf(buf
, sizeof(buf
), INT64_FORMAT
" MB",
388 (size
+ mult
/ 2) / mult
);
392 if (size
< limit
* mult
)
393 snprintf(buf
, sizeof(buf
), INT64_FORMAT
" GB",
394 (size
+ mult
/ 2) / mult
);
398 snprintf(buf
, sizeof(buf
), INT64_FORMAT
" TB",
399 (size
+ mult
/ 2) / mult
);
405 PG_RETURN_TEXT_P(cstring_to_text(buf
));