]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/dbsize.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[postgresql] / src / backend / utils / adt / dbsize.c
1 /*
2  * dbsize.c
3  *              object size functions
4  *
5  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
6  *
7  * IDENTIFICATION
8  *        $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.10 2007/01/05 22:19:40 momjian Exp $
9  *
10  */
11
12 #include "postgres.h"
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16
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/builtins.h"
26 #include "utils/syscache.h"
27 #include "utils/relcache.h"
28
29
30 /* Return physical size of directory contents, or 0 if dir doesn't exist */
31 static int64
32 db_dir_size(const char *path)
33 {
34         int64           dirsize = 0;
35         struct dirent *direntry;
36         DIR                *dirdesc;
37         char            filename[MAXPGPATH];
38
39         dirdesc = AllocateDir(path);
40
41         if (!dirdesc)
42                 return 0;
43
44         while ((direntry = ReadDir(dirdesc, path)) != NULL)
45         {
46                 struct stat fst;
47
48                 if (strcmp(direntry->d_name, ".") == 0 ||
49                         strcmp(direntry->d_name, "..") == 0)
50                         continue;
51
52                 snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
53
54                 if (stat(filename, &fst) < 0)
55                         ereport(ERROR,
56                                         (errcode_for_file_access(),
57                                          errmsg("could not stat file \"%s\": %m", filename)));
58
59                 dirsize += fst.st_size;
60         }
61
62         FreeDir(dirdesc);
63         return dirsize;
64 }
65
66 /*
67  * calculate size of database in all tablespaces
68  */
69 static int64
70 calculate_database_size(Oid dbOid)
71 {
72         int64           totalsize;
73         DIR                *dirdesc;
74         struct dirent *direntry;
75         char            dirpath[MAXPGPATH];
76         char            pathname[MAXPGPATH];
77
78         /* Shared storage in pg_global is not counted */
79
80         /* Include pg_default storage */
81         snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
82         totalsize = db_dir_size(pathname);
83
84         /* Scan the non-default tablespaces */
85         snprintf(dirpath, MAXPGPATH, "pg_tblspc");
86         dirdesc = AllocateDir(dirpath);
87         if (!dirdesc)
88                 ereport(ERROR,
89                                 (errcode_for_file_access(),
90                                  errmsg("could not open tablespace directory \"%s\": %m",
91                                                 dirpath)));
92
93         while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
94         {
95                 if (strcmp(direntry->d_name, ".") == 0 ||
96                         strcmp(direntry->d_name, "..") == 0)
97                         continue;
98
99                 snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u",
100                                  direntry->d_name, dbOid);
101                 totalsize += db_dir_size(pathname);
102         }
103
104         FreeDir(dirdesc);
105
106         /* Complain if we found no trace of the DB at all */
107         if (!totalsize)
108                 ereport(ERROR,
109                                 (ERRCODE_UNDEFINED_DATABASE,
110                                  errmsg("database with OID %u does not exist", dbOid)));
111
112         return totalsize;
113 }
114
115 Datum
116 pg_database_size_oid(PG_FUNCTION_ARGS)
117 {
118         Oid                     dbOid = PG_GETARG_OID(0);
119
120         PG_RETURN_INT64(calculate_database_size(dbOid));
121 }
122
123 Datum
124 pg_database_size_name(PG_FUNCTION_ARGS)
125 {
126         Name            dbName = PG_GETARG_NAME(0);
127         Oid                     dbOid = get_database_oid(NameStr(*dbName));
128
129         if (!OidIsValid(dbOid))
130                 ereport(ERROR,
131                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
132                                  errmsg("database \"%s\" does not exist",
133                                                 NameStr(*dbName))));
134
135         PG_RETURN_INT64(calculate_database_size(dbOid));
136 }
137
138
139 /*
140  * calculate total size of tablespace
141  */
142 static int64
143 calculate_tablespace_size(Oid tblspcOid)
144 {
145         char            tblspcPath[MAXPGPATH];
146         char            pathname[MAXPGPATH];
147         int64           totalsize = 0;
148         DIR                *dirdesc;
149         struct dirent *direntry;
150
151         if (tblspcOid == DEFAULTTABLESPACE_OID)
152                 snprintf(tblspcPath, MAXPGPATH, "base");
153         else if (tblspcOid == GLOBALTABLESPACE_OID)
154                 snprintf(tblspcPath, MAXPGPATH, "global");
155         else
156                 snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u", tblspcOid);
157
158         dirdesc = AllocateDir(tblspcPath);
159
160         if (!dirdesc)
161                 ereport(ERROR,
162                                 (errcode_for_file_access(),
163                                  errmsg("could not open tablespace directory \"%s\": %m",
164                                                 tblspcPath)));
165
166         while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
167         {
168                 struct stat fst;
169
170                 if (strcmp(direntry->d_name, ".") == 0 ||
171                         strcmp(direntry->d_name, "..") == 0)
172                         continue;
173
174                 snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
175
176                 if (stat(pathname, &fst) < 0)
177                         ereport(ERROR,
178                                         (errcode_for_file_access(),
179                                          errmsg("could not stat file \"%s\": %m", pathname)));
180
181                 if (fst.st_mode & S_IFDIR)
182                         totalsize += db_dir_size(pathname);
183
184                 totalsize += fst.st_size;
185         }
186
187         FreeDir(dirdesc);
188
189         return totalsize;
190 }
191
192 Datum
193 pg_tablespace_size_oid(PG_FUNCTION_ARGS)
194 {
195         Oid                     tblspcOid = PG_GETARG_OID(0);
196
197         PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
198 }
199
200 Datum
201 pg_tablespace_size_name(PG_FUNCTION_ARGS)
202 {
203         Name            tblspcName = PG_GETARG_NAME(0);
204         Oid                     tblspcOid = get_tablespace_oid(NameStr(*tblspcName));
205
206         if (!OidIsValid(tblspcOid))
207                 ereport(ERROR,
208                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
209                                  errmsg("tablespace \"%s\" does not exist",
210                                                 NameStr(*tblspcName))));
211
212         PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
213 }
214
215
216 /*
217  * calculate size of a relation
218  */
219 static int64
220 calculate_relation_size(RelFileNode *rfn)
221 {
222         int64           totalsize = 0;
223         char       *relationpath;
224         char            pathname[MAXPGPATH];
225         unsigned int segcount = 0;
226
227         relationpath = relpath(*rfn);
228
229         for (segcount = 0;; segcount++)
230         {
231                 struct stat fst;
232
233                 if (segcount == 0)
234                         snprintf(pathname, MAXPGPATH, "%s",
235                                          relationpath);
236                 else
237                         snprintf(pathname, MAXPGPATH, "%s.%u",
238                                          relationpath, segcount);
239
240                 if (stat(pathname, &fst) < 0)
241                 {
242                         if (errno == ENOENT)
243                                 break;
244                         else
245                                 ereport(ERROR,
246                                                 (errcode_for_file_access(),
247                                                  errmsg("could not stat file \"%s\": %m", pathname)));
248                 }
249                 totalsize += fst.st_size;
250         }
251
252         return totalsize;
253 }
254
255 Datum
256 pg_relation_size_oid(PG_FUNCTION_ARGS)
257 {
258         Oid                     relOid = PG_GETARG_OID(0);
259         Relation        rel;
260         int64           size;
261
262         rel = relation_open(relOid, AccessShareLock);
263
264         size = calculate_relation_size(&(rel->rd_node));
265
266         relation_close(rel, AccessShareLock);
267
268         PG_RETURN_INT64(size);
269 }
270
271 Datum
272 pg_relation_size_name(PG_FUNCTION_ARGS)
273 {
274         text       *relname = PG_GETARG_TEXT_P(0);
275         RangeVar   *relrv;
276         Relation        rel;
277         int64           size;
278
279         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
280         rel = relation_openrv(relrv, AccessShareLock);
281
282         size = calculate_relation_size(&(rel->rd_node));
283
284         relation_close(rel, AccessShareLock);
285
286         PG_RETURN_INT64(size);
287 }
288
289
290 /*
291  *      Compute the on-disk size of files for the relation according to the
292  *      stat function, including heap data, index data, and toast data.
293  */
294 static int64
295 calculate_total_relation_size(Oid Relid)
296 {
297         Relation        heapRel;
298         Oid                     toastOid;
299         int64           size;
300         ListCell   *cell;
301
302         heapRel = relation_open(Relid, AccessShareLock);
303         toastOid = heapRel->rd_rel->reltoastrelid;
304
305         /* Get the heap size */
306         size = calculate_relation_size(&(heapRel->rd_node));
307
308         /* Include any dependent indexes */
309         if (heapRel->rd_rel->relhasindex)
310         {
311                 List       *index_oids = RelationGetIndexList(heapRel);
312
313                 foreach(cell, index_oids)
314                 {
315                         Oid                     idxOid = lfirst_oid(cell);
316                         Relation        iRel;
317
318                         iRel = relation_open(idxOid, AccessShareLock);
319
320                         size += calculate_relation_size(&(iRel->rd_node));
321
322                         relation_close(iRel, AccessShareLock);
323                 }
324
325                 list_free(index_oids);
326         }
327
328         /* Recursively include toast table (and index) size */
329         if (OidIsValid(toastOid))
330                 size += calculate_total_relation_size(toastOid);
331
332         relation_close(heapRel, AccessShareLock);
333
334         return size;
335 }
336
337 Datum
338 pg_total_relation_size_oid(PG_FUNCTION_ARGS)
339 {
340         Oid                     relid = PG_GETARG_OID(0);
341
342         PG_RETURN_INT64(calculate_total_relation_size(relid));
343 }
344
345 Datum
346 pg_total_relation_size_name(PG_FUNCTION_ARGS)
347 {
348         text       *relname = PG_GETARG_TEXT_P(0);
349         RangeVar   *relrv;
350         Oid                     relid;
351
352         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
353         relid = RangeVarGetRelid(relrv, false);
354
355         PG_RETURN_INT64(calculate_total_relation_size(relid));
356 }
357
358 /*
359  * formatting with size units
360  */
361 Datum
362 pg_size_pretty(PG_FUNCTION_ARGS)
363 {
364         int64           size = PG_GETARG_INT64(0);
365         char       *result = palloc(50 + VARHDRSZ);
366         int64           limit = 10 * 1024;
367         int64           mult = 1;
368
369         if (size < limit * mult)
370                 snprintf(VARDATA(result), 50, INT64_FORMAT " bytes", size);
371         else
372         {
373                 mult *= 1024;
374                 if (size < limit * mult)
375                         snprintf(VARDATA(result), 50, INT64_FORMAT " kB",
376                                          (size + mult / 2) / mult);
377                 else
378                 {
379                         mult *= 1024;
380                         if (size < limit * mult)
381                                 snprintf(VARDATA(result), 50, INT64_FORMAT " MB",
382                                                  (size + mult / 2) / mult);
383                         else
384                         {
385                                 mult *= 1024;
386                                 if (size < limit * mult)
387                                         snprintf(VARDATA(result), 50, INT64_FORMAT " GB",
388                                                          (size + mult / 2) / mult);
389                                 else
390                                 {
391                                         mult *= 1024;
392                                         snprintf(VARDATA(result), 50, INT64_FORMAT " TB",
393                                                          (size + mult / 2) / mult);
394                                 }
395                         }
396                 }
397         }
398
399         VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
400
401         PG_RETURN_TEXT_P(result);
402 }