Use fork names instead of numbers in the file names for additional
[PostgreSQL.git] / src / backend / utils / adt / dbsize.c
blobe4dedde67c3b6ca597193b48deca38d2cca6790b
1 /*
2 * dbsize.c
3 * object size functions
5 * Copyright (c) 2002-2008, PostgreSQL Global Development Group
7 * IDENTIFICATION
8 * $PostgreSQL$
12 #include "postgres.h"
14 #include <sys/types.h>
15 #include <sys/stat.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 */
32 static int64
33 db_dir_size(const char *path)
35 int64 dirsize = 0;
36 struct dirent *direntry;
37 DIR *dirdesc;
38 char filename[MAXPGPATH];
40 dirdesc = AllocateDir(path);
42 if (!dirdesc)
43 return 0;
45 while ((direntry = ReadDir(dirdesc, path)) != NULL)
47 struct stat fst;
49 if (strcmp(direntry->d_name, ".") == 0 ||
50 strcmp(direntry->d_name, "..") == 0)
51 continue;
53 snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
55 if (stat(filename, &fst) < 0)
57 if (errno == ENOENT)
58 continue;
59 else
60 ereport(ERROR,
61 (errcode_for_file_access(),
62 errmsg("could not stat file \"%s\": %m", filename)));
64 dirsize += fst.st_size;
67 FreeDir(dirdesc);
68 return dirsize;
72 * calculate size of database in all tablespaces
74 static int64
75 calculate_database_size(Oid dbOid)
77 int64 totalsize;
78 DIR *dirdesc;
79 struct dirent *direntry;
80 char dirpath[MAXPGPATH];
81 char pathname[MAXPGPATH];
82 AclResult aclresult;
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);
99 if (!dirdesc)
100 ereport(ERROR,
101 (errcode_for_file_access(),
102 errmsg("could not open tablespace directory \"%s\": %m",
103 dirpath)));
105 while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
107 if (strcmp(direntry->d_name, ".") == 0 ||
108 strcmp(direntry->d_name, "..") == 0)
109 continue;
111 snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u",
112 direntry->d_name, dbOid);
113 totalsize += db_dir_size(pathname);
116 FreeDir(dirdesc);
118 /* Complain if we found no trace of the DB at all */
119 if (!totalsize)
120 ereport(ERROR,
121 (ERRCODE_UNDEFINED_DATABASE,
122 errmsg("database with OID %u does not exist", dbOid)));
124 return totalsize;
127 Datum
128 pg_database_size_oid(PG_FUNCTION_ARGS)
130 Oid dbOid = PG_GETARG_OID(0);
132 PG_RETURN_INT64(calculate_database_size(dbOid));
135 Datum
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))
142 ereport(ERROR,
143 (errcode(ERRCODE_UNDEFINED_DATABASE),
144 errmsg("database \"%s\" does not exist",
145 NameStr(*dbName))));
147 PG_RETURN_INT64(calculate_database_size(dbOid));
152 * calculate total size of tablespace
154 static int64
155 calculate_tablespace_size(Oid tblspcOid)
157 char tblspcPath[MAXPGPATH];
158 char pathname[MAXPGPATH];
159 int64 totalsize = 0;
160 DIR *dirdesc;
161 struct dirent *direntry;
162 AclResult aclresult;
165 * User must have CREATE privilege for target tablespace, either
166 * explicitly granted or implicitly because it is default for current
167 * database.
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");
181 else
182 snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u", tblspcOid);
184 dirdesc = AllocateDir(tblspcPath);
186 if (!dirdesc)
187 ereport(ERROR,
188 (errcode_for_file_access(),
189 errmsg("could not open tablespace directory \"%s\": %m",
190 tblspcPath)));
192 while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
194 struct stat fst;
196 if (strcmp(direntry->d_name, ".") == 0 ||
197 strcmp(direntry->d_name, "..") == 0)
198 continue;
200 snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
202 if (stat(pathname, &fst) < 0)
204 if (errno == ENOENT)
205 continue;
206 else
207 ereport(ERROR,
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;
218 FreeDir(dirdesc);
220 return totalsize;
223 Datum
224 pg_tablespace_size_oid(PG_FUNCTION_ARGS)
226 Oid tblspcOid = PG_GETARG_OID(0);
228 PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
231 Datum
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))
238 ereport(ERROR,
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
250 static int64
251 calculate_relation_size(RelFileNode *rfn, ForkNumber forknum)
253 int64 totalsize = 0;
254 char *relationpath;
255 char pathname[MAXPGPATH];
256 unsigned int segcount = 0;
258 relationpath = relpath(*rfn, forknum);
260 for (segcount = 0;; segcount++)
262 struct stat fst;
264 if (segcount == 0)
265 snprintf(pathname, MAXPGPATH, "%s",
266 relationpath);
267 else
268 snprintf(pathname, MAXPGPATH, "%s.%u",
269 relationpath, segcount);
271 if (stat(pathname, &fst) < 0)
273 if (errno == ENOENT)
274 break;
275 else
276 ereport(ERROR,
277 (errcode_for_file_access(),
278 errmsg("could not stat file \"%s\": %m", pathname)));
280 totalsize += fst.st_size;
283 return totalsize;
286 Datum
287 pg_relation_size(PG_FUNCTION_ARGS)
289 Oid relOid = PG_GETARG_OID(0);
290 text *forkName = PG_GETARG_TEXT_P(1);
291 Relation rel;
292 int64 size;
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.
309 static int64
310 calculate_total_relation_size(Oid Relid)
312 Relation heapRel;
313 Oid toastOid;
314 int64 size;
315 ListCell *cell;
316 ForkNumber forkNum;
318 heapRel = relation_open(Relid, AccessShareLock);
319 toastOid = heapRel->rd_rel->reltoastrelid;
321 /* Get the heap size */
322 size = 0;
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);
334 Relation iRel;
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);
353 return size;
356 Datum
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
367 Datum
368 pg_size_pretty(PG_FUNCTION_ARGS)
370 int64 size = PG_GETARG_INT64(0);
371 char buf[64];
372 int64 limit = 10 * 1024;
373 int64 mult = 1;
375 if (size < limit * mult)
376 snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
377 else
379 mult *= 1024;
380 if (size < limit * mult)
381 snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
382 (size + mult / 2) / mult);
383 else
385 mult *= 1024;
386 if (size < limit * mult)
387 snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
388 (size + mult / 2) / mult);
389 else
391 mult *= 1024;
392 if (size < limit * mult)
393 snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
394 (size + mult / 2) / mult);
395 else
397 mult *= 1024;
398 snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
399 (size + mult / 2) / mult);
405 PG_RETURN_TEXT_P(cstring_to_text(buf));