Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

dbsize.c

/*
 * dbsize.c
 *          object size functions
 *
 * Copyright (c) 2002-2009, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/utils/adt/dbsize.c,v 1.23 2009/01/01 17:23:49 momjian Exp $
 *
 */

#include "postgres.h"

#include <sys/types.h>
#include <sys/stat.h>

#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
#include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"


/* Return physical size of directory contents, or 0 if dir doesn't exist */
static int64
db_dir_size(const char *path)
{
      int64       dirsize = 0;
      struct dirent *direntry;
      DIR            *dirdesc;
      char        filename[MAXPGPATH];

      dirdesc = AllocateDir(path);

      if (!dirdesc)
            return 0;

      while ((direntry = ReadDir(dirdesc, path)) != NULL)
      {
            struct stat fst;

            if (strcmp(direntry->d_name, ".") == 0 ||
                  strcmp(direntry->d_name, "..") == 0)
                  continue;

            snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);

            if (stat(filename, &fst) < 0)
            {
                  if (errno == ENOENT)
                        continue;
                  else
                        ereport(ERROR,
                                    (errcode_for_file_access(),
                                     errmsg("could not stat file \"%s\": %m", filename)));
            }
            dirsize += fst.st_size;
      }

      FreeDir(dirdesc);
      return dirsize;
}

/*
 * calculate size of database in all tablespaces
 */
static int64
calculate_database_size(Oid dbOid)
{
      int64       totalsize;
      DIR            *dirdesc;
      struct dirent *direntry;
      char        dirpath[MAXPGPATH];
      char        pathname[MAXPGPATH];
      AclResult   aclresult;

      /* User must have connect privilege for target database */
      aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT);
      if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_DATABASE,
                                 get_database_name(dbOid));

      /* Shared storage in pg_global is not counted */

      /* Include pg_default storage */
      snprintf(pathname, MAXPGPATH, "base/%u", dbOid);
      totalsize = db_dir_size(pathname);

      /* Scan the non-default tablespaces */
      snprintf(dirpath, MAXPGPATH, "pg_tblspc");
      dirdesc = AllocateDir(dirpath);
      if (!dirdesc)
            ereport(ERROR,
                        (errcode_for_file_access(),
                         errmsg("could not open tablespace directory \"%s\": %m",
                                    dirpath)));

      while ((direntry = ReadDir(dirdesc, dirpath)) != NULL)
      {
            if (strcmp(direntry->d_name, ".") == 0 ||
                  strcmp(direntry->d_name, "..") == 0)
                  continue;

            snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u",
                         direntry->d_name, dbOid);
            totalsize += db_dir_size(pathname);
      }

      FreeDir(dirdesc);

      /* Complain if we found no trace of the DB at all */
      if (!totalsize)
            ereport(ERROR,
                        (ERRCODE_UNDEFINED_DATABASE,
                         errmsg("database with OID %u does not exist", dbOid)));

      return totalsize;
}

Datum
pg_database_size_oid(PG_FUNCTION_ARGS)
{
      Oid               dbOid = PG_GETARG_OID(0);

      PG_RETURN_INT64(calculate_database_size(dbOid));
}

Datum
pg_database_size_name(PG_FUNCTION_ARGS)
{
      Name        dbName = PG_GETARG_NAME(0);
      Oid               dbOid = get_database_oid(NameStr(*dbName));

      if (!OidIsValid(dbOid))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_DATABASE),
                         errmsg("database \"%s\" does not exist",
                                    NameStr(*dbName))));

      PG_RETURN_INT64(calculate_database_size(dbOid));
}


/*
 * calculate total size of tablespace
 */
static int64
calculate_tablespace_size(Oid tblspcOid)
{
      char        tblspcPath[MAXPGPATH];
      char        pathname[MAXPGPATH];
      int64       totalsize = 0;
      DIR            *dirdesc;
      struct dirent *direntry;
      AclResult   aclresult;

      /*
       * User must have CREATE privilege for target tablespace, either
       * explicitly granted or implicitly because it is default for current
       * database.
       */
      if (tblspcOid != MyDatabaseTableSpace)
      {
            aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
            if (aclresult != ACLCHECK_OK)
                  aclcheck_error(aclresult, ACL_KIND_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, "pg_tblspc/%u", tblspcOid);

      dirdesc = AllocateDir(tblspcPath);

      if (!dirdesc)
            ereport(ERROR,
                        (errcode_for_file_access(),
                         errmsg("could not open tablespace directory \"%s\": %m",
                                    tblspcPath)));

      while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
      {
            struct stat fst;

            if (strcmp(direntry->d_name, ".") == 0 ||
                  strcmp(direntry->d_name, "..") == 0)
                  continue;

            snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);

            if (stat(pathname, &fst) < 0)
            {
                  if (errno == ENOENT)
                        continue;
                  else
                        ereport(ERROR,
                                    (errcode_for_file_access(),
                                     errmsg("could not stat file \"%s\": %m", pathname)));
            }

            if (S_ISDIR(fst.st_mode))
                  totalsize += db_dir_size(pathname);

            totalsize += fst.st_size;
      }

      FreeDir(dirdesc);

      return totalsize;
}

Datum
pg_tablespace_size_oid(PG_FUNCTION_ARGS)
{
      Oid               tblspcOid = PG_GETARG_OID(0);

      PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
}

Datum
pg_tablespace_size_name(PG_FUNCTION_ARGS)
{
      Name        tblspcName = PG_GETARG_NAME(0);
      Oid               tblspcOid = get_tablespace_oid(NameStr(*tblspcName));

      if (!OidIsValid(tblspcOid))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("tablespace \"%s\" does not exist",
                                    NameStr(*tblspcName))));

      PG_RETURN_INT64(calculate_tablespace_size(tblspcOid));
}


/*
 * calculate size of a relation
 */
static int64
calculate_relation_size(RelFileNode *rfn, ForkNumber forknum)
{
      int64       totalsize = 0;
      char     *relationpath;
      char        pathname[MAXPGPATH];
      unsigned int segcount = 0;

      relationpath = relpath(*rfn, forknum);

      for (segcount = 0;; segcount++)
      {
            struct stat fst;

            if (segcount == 0)
                  snprintf(pathname, MAXPGPATH, "%s",
                               relationpath);
            else
                  snprintf(pathname, MAXPGPATH, "%s.%u",
                               relationpath, segcount);

            if (stat(pathname, &fst) < 0)
            {
                  if (errno == ENOENT)
                        break;
                  else
                        ereport(ERROR,
                                    (errcode_for_file_access(),
                                     errmsg("could not stat file \"%s\": %m", pathname)));
            }
            totalsize += fst.st_size;
      }

      return totalsize;
}

Datum
pg_relation_size(PG_FUNCTION_ARGS)
{
      Oid               relOid = PG_GETARG_OID(0);
      text     *forkName = PG_GETARG_TEXT_P(1);
      Relation    rel;
      int64       size;

      rel = relation_open(relOid, AccessShareLock);

      size = calculate_relation_size(&(rel->rd_node),
                                             forkname_to_number(text_to_cstring(forkName)));

      relation_close(rel, AccessShareLock);

      PG_RETURN_INT64(size);
}


/*
 *    Compute the on-disk size of files for the relation according to the
 *    stat function, including heap data, index data, and toast data.
 */
static int64
calculate_total_relation_size(Oid Relid)
{
      Relation    heapRel;
      Oid               toastOid;
      int64       size;
      ListCell   *cell;
      ForkNumber  forkNum;

      heapRel = relation_open(Relid, AccessShareLock);
      toastOid = heapRel->rd_rel->reltoastrelid;

      /* Get the heap size */
      size = 0;
      for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
            size += calculate_relation_size(&(heapRel->rd_node), forkNum);

      /* Include any dependent indexes */
      if (heapRel->rd_rel->relhasindex)
      {
            List     *index_oids = RelationGetIndexList(heapRel);

            foreach(cell, index_oids)
            {
                  Oid               idxOid = lfirst_oid(cell);
                  Relation    iRel;

                  iRel = relation_open(idxOid, AccessShareLock);

                  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
                        size += calculate_relation_size(&(iRel->rd_node), forkNum);

                  relation_close(iRel, AccessShareLock);
            }

            list_free(index_oids);
      }

      /* Recursively include toast table (and index) size */
      if (OidIsValid(toastOid))
            size += calculate_total_relation_size(toastOid);

      relation_close(heapRel, AccessShareLock);

      return size;
}

Datum
pg_total_relation_size(PG_FUNCTION_ARGS)
{
      Oid               relid = PG_GETARG_OID(0);

      PG_RETURN_INT64(calculate_total_relation_size(relid));
}

/*
 * formatting with size units
 */
Datum
pg_size_pretty(PG_FUNCTION_ARGS)
{
      int64       size = PG_GETARG_INT64(0);
      char        buf[64];
      int64       limit = 10 * 1024;
      int64       mult = 1;

      if (size < limit * mult)
            snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
      else
      {
            mult *= 1024;
            if (size < limit * mult)
                  snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
                               (size + mult / 2) / mult);
            else
            {
                  mult *= 1024;
                  if (size < limit * mult)
                        snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
                                     (size + mult / 2) / mult);
                  else
                  {
                        mult *= 1024;
                        if (size < limit * mult)
                              snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
                                           (size + mult / 2) / mult);
                        else
                        {
                              mult *= 1024;
                              snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
                                           (size + mult / 2) / mult);
                        }
                  }
            }
      }

      PG_RETURN_TEXT_P(cstring_to_text(buf));
}

Generated by  Doxygen 1.6.0   Back to index