forked from huawei/openGauss-server
2713 lines
86 KiB
C++
2713 lines
86 KiB
C++
/*
|
|
* dbsize.c
|
|
* Database object size functions, and related inquiries
|
|
*
|
|
* Copyright (c) 2002-2012, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/adt/dbsize.c
|
|
*
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include "access/cstore_am.h"
|
|
#include "access/heapam.h"
|
|
#include "access/tableam.h"
|
|
#include "access/htup.h"
|
|
#include "access/reloptions.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/dfsstore_ctlg.h"
|
|
#include "catalog/gs_matview.h"
|
|
#include "catalog/namespace.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "catalog/pg_statistic.h"
|
|
#include "catalog/pg_hashbucket_fn.h"
|
|
#include "commands/defrem.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "commands/matview.h"
|
|
#include "commands/tablespace.h"
|
|
#include "executor/spi.h"
|
|
#include "miscadmin.h"
|
|
#ifdef ENABLE_MOT
|
|
#include "foreign/fdwapi.h"
|
|
#endif
|
|
#ifdef PGXC
|
|
#include "catalog/pgxc_node.h"
|
|
#include "pgxc/nodemgr.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "pgxc/pgxcnode.h"
|
|
#include "pgxc/execRemote.h"
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
#include "tsdb/cache/partid_cachemgr.h"
|
|
#include "tsdb/utils/ts_relcache.h"
|
|
#include "tsdb/common/ts_tablecmds.h"
|
|
#endif
|
|
#endif
|
|
#include "storage/fd.h"
|
|
#include "threadpool/threadpool.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/numeric.h"
|
|
#include "utils/numeric_gs.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/relfilenodemap.h"
|
|
#include "utils/relmapper.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "catalog/pg_partition_fn.h"
|
|
#include "utils/partitionmap.h"
|
|
#include "utils/partitionmap_gs.h"
|
|
#include "storage/cu.h"
|
|
#include "storage/custorage.h"
|
|
#include "storage/cstore/cstore_compress.h"
|
|
#include "vecexecutor/vecnodes.h"
|
|
|
|
#ifdef PGXC
|
|
static Datum pgxc_database_size(Oid dbOid);
|
|
static Datum pgxc_source_database_size();
|
|
static Datum pgxc_tablespace_size(Oid tbOid);
|
|
static int64 pgxc_exec_sizefunc(Oid relOid, char* funcname, char* extra_arg);
|
|
static int64 pgxc_exec_partition_sizefunc(Oid partTableOid, Oid partOid, char* funcName);
|
|
static int64 calculate_partition_size(Oid partTableOid, Oid partOid);
|
|
int64 CalculateCStoreRelationSize(Relation rel, ForkNumber forknum);
|
|
int64 CalculateCStorePartitionedRelationSize(Relation rel, ForkNumber forknum);
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
int64 CalculateTStorePartitionedRelationSize(Relation rel, ForkNumber forknum);
|
|
#endif
|
|
static int64 calculate_partition_indexes_size(Oid partTableOid, Oid partOid);
|
|
Oid pg_toast_get_baseid(Oid relOid, bool* isPartToast);
|
|
bool IsToastRelationbyOid(Oid relid);
|
|
static bool IsIndexRelationbyOid(Oid relid);
|
|
static int64 calculate_toast_table_size(Oid toastrelid);
|
|
static int64 calculate_dir_size_on_dfs(Oid tblSpcOid, StringInfo dirPath);
|
|
static int64 calculate_dbsize_on_dfs(Oid dbOid);
|
|
static int64 calculate_tablespace_size_on_dfs(Oid tblSpcOid);
|
|
static int64 calculate_table_file_size(Relation rel, bool isCStore, int forkNumOption);
|
|
static void AddRemoteToastBuf(bool isPartToast, Relation rel, char* funcname, char* extra_arg,
|
|
Relation partTableRel, Partition part, StringInfo buf);
|
|
static void AddRemoteMatviewBuf(Relation rel, char* funcname, char* extra_arg,
|
|
MvRelationType matviewRelationType, StringInfo buf);
|
|
#define DEFAULT_FORKNUM -1
|
|
|
|
/*
|
|
* Below macro is important when the object size functions are called
|
|
* for system catalog tables. For pg_catalog tables and other Coordinator-only
|
|
* tables, we should return the data from Coordinator. If we don't find
|
|
* locator info, that means it is a Coordinator-only table.
|
|
*/
|
|
#define COLLECT_FROM_DATANODES(relid) \
|
|
((IS_PGXC_COORDINATOR && !IsConnFromCoord() && !is_sys_table(relid) && \
|
|
((GetRelationLocInfo((relid)) != NULL) || IsToastRelationbyOid(relid) || IsIndexRelationbyOid(relid))))
|
|
#endif
|
|
|
|
/* 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 = NULL;
|
|
DIR* dirdesc = NULL;
|
|
char filename[MAXPGPATH] = {'\0'};
|
|
|
|
dirdesc = AllocateDir(path);
|
|
|
|
if (unlikely(NULL == dirdesc))
|
|
return 0;
|
|
|
|
while ((direntry = ReadDir(dirdesc, path)) != NULL) {
|
|
struct stat fst;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
if (unlikely(strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0))
|
|
continue;
|
|
|
|
errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH - 1, "%s/%s", path, direntry->d_name);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
|
|
if (unlikely(stat(filename, &fst) < 0)) {
|
|
if (errno == ENOENT)
|
|
continue;
|
|
else
|
|
ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", filename)));
|
|
}
|
|
if (S_ISDIR(fst.st_mode)) {
|
|
dirsize += db_dir_size(filename);
|
|
} else {
|
|
dirsize += fst.st_size;
|
|
}
|
|
}
|
|
|
|
FreeDir(dirdesc);
|
|
return dirsize;
|
|
}
|
|
|
|
/**
|
|
* @Description: calculate tablespace size on DFS.
|
|
* @in tblSpcOid, the tablespace oid.
|
|
* @out none.
|
|
* @return If the type of specified tablespace is HDFS, return
|
|
* real size, otherwise return 0.
|
|
*/
|
|
static int64 calculate_tablespace_size_on_dfs(Oid tblSpcOid)
|
|
{
|
|
int64 dirSize = 0;
|
|
StringInfo StorePath;
|
|
|
|
if (!IsSpecifiedTblspc(tblSpcOid, FILESYSTEM_HDFS)) {
|
|
return dirSize;
|
|
}
|
|
StorePath = getHDFSTblSpcStorePath(tblSpcOid);
|
|
|
|
dirSize = calculate_dir_size_on_dfs(tblSpcOid, StorePath);
|
|
|
|
pfree_ext(StorePath->data);
|
|
pfree_ext(StorePath);
|
|
return dirSize;
|
|
}
|
|
|
|
/**
|
|
* @Description: calculate database size on DFS.
|
|
* @in dbOid, the database oid.
|
|
* @out none.
|
|
* @return If the database exists on DFS, return real size,
|
|
* otherwise return 0.
|
|
*/
|
|
static int64 calculate_dbsize_on_dfs(Oid dbOid)
|
|
{
|
|
List* tblSpcList = HDFSTablespaceDirExistDatabase(dbOid);
|
|
ListCell* lc = NULL;
|
|
int64 dirSize = 0;
|
|
|
|
if (NIL == tblSpcList) {
|
|
return dirSize;
|
|
}
|
|
|
|
foreach (lc, tblSpcList) {
|
|
StringInfo tblspcInfo = (StringInfo)lfirst(lc);
|
|
Oid tblSpcOid = get_tablespace_oid(tblspcInfo->data, false);
|
|
|
|
StringInfo StorePath = getHDFSTblSpcStorePath(tblSpcOid);
|
|
|
|
appendStringInfo(StorePath, "/%s", get_and_check_db_name(dbOid, true));
|
|
|
|
dirSize += calculate_dir_size_on_dfs(tblSpcOid, StorePath);
|
|
|
|
pfree_ext(StorePath->data);
|
|
pfree_ext(StorePath);
|
|
pfree_ext(tblspcInfo);
|
|
}
|
|
list_free_ext(tblSpcList);
|
|
return dirSize;
|
|
}
|
|
|
|
/**
|
|
* @Description: calculate DFS directory size.
|
|
* @in tblSpcOid, the specified tablespace oid.
|
|
* @out dirPath, the directory to be calculated.
|
|
* @return return the directory size.
|
|
*/
|
|
static int64 calculate_dir_size_on_dfs(Oid tblSpcOid, StringInfo dirPath)
|
|
{
|
|
FEATURE_NOT_PUBLIC_ERROR("HDFS is not yet supported.");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* calculate size of database in all tablespaces
|
|
*/
|
|
static int64 calculate_database_size(Oid dbOid)
|
|
{
|
|
int64 totalsize;
|
|
DIR* dirdesc = NULL;
|
|
struct dirent* direntry = NULL;
|
|
char dirpath[MAXPGPATH] = {'\0'};
|
|
char pathname[MAXPGPATH] = {'\0'};
|
|
AclResult aclresult;
|
|
List* existTblSpcList = NIL;
|
|
errno_t rc = EOK;
|
|
|
|
/* 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_and_check_db_name(dbOid));
|
|
existTblSpcList = HDFSTablespaceDirExistDatabase(dbOid);
|
|
if (IS_PGXC_DATANODE && IsConnFromApp() && NIL != existTblSpcList) {
|
|
StringInfo existTblspc = (StringInfo)linitial(existTblSpcList);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OBJECT_IN_USE),
|
|
errmsg("It is unsupported to calculate size of database \"%s\""
|
|
" under the DFS tablespace \"%s\" on data node.",
|
|
get_and_check_db_name(dbOid),
|
|
existTblspc->data),
|
|
errdetail("Please calculate size of database \"%s\" on coordinator node.", get_and_check_db_name(dbOid))));
|
|
}
|
|
|
|
/* Shared storage in pg_global is not counted */
|
|
|
|
/* Include pg_default storage */
|
|
rc = snprintf_s(pathname, MAXPGPATH, MAXPGPATH - 1, "base/%u", dbOid);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
totalsize = db_dir_size(pathname);
|
|
|
|
/* Scan the non-default tablespaces */
|
|
rc = snprintf_s(dirpath, MAXPGPATH, MAXPGPATH - 1, "pg_tblspc");
|
|
securec_check_ss(rc, "\0", "\0");
|
|
dirdesc = AllocateDir(dirpath);
|
|
if (NULL == dirdesc)
|
|
ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", dirpath)));
|
|
|
|
while ((direntry = ReadDir(dirdesc, dirpath)) != NULL) {
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0)
|
|
continue;
|
|
|
|
#ifdef PGXC
|
|
/* Postgres-XC tablespaces include node name in path */
|
|
rc = snprintf_s(pathname,
|
|
MAXPGPATH,
|
|
MAXPGPATH - 1,
|
|
"pg_tblspc/%s/%s_%s/%u",
|
|
direntry->d_name,
|
|
TABLESPACE_VERSION_DIRECTORY,
|
|
g_instance.attr.attr_common.PGXCNodeName,
|
|
dbOid);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
|
|
#else
|
|
rc = snprintf_s(pathname,
|
|
MAXPGPATH,
|
|
MAXPGPATH - 1,
|
|
"pg_tblspc/%s/%s/%u",
|
|
direntry->d_name,
|
|
TABLESPACE_VERSION_DIRECTORY,
|
|
dbOid);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
#endif
|
|
totalsize += db_dir_size(pathname);
|
|
}
|
|
|
|
FreeDir(dirdesc);
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/*
|
|
* @Description: calculate the compress ratio of the column-partition relation.
|
|
* @in onerel: the relation.
|
|
* @return: the source size of this relation.
|
|
*/
|
|
static double calculate_coltable_compress_ratio(Relation onerel)
|
|
{
|
|
CStoreScanDesc cstoreScanDesc = NULL;
|
|
TupleDesc tupdesc = onerel->rd_att;
|
|
int attrNum = tupdesc->natts;
|
|
Form_pg_attribute* attrs = tupdesc->attrs;
|
|
CUDesc cuDesc;
|
|
CU* cuPtr = NULL;
|
|
double total_source_size = 0;
|
|
double total_cu_size = 0;
|
|
BlockNumber targetblock = FirstCUID + 1;
|
|
|
|
AttrNumber* colIdx = (AttrNumber*)palloc0(sizeof(AttrNumber) * attrNum);
|
|
int* slotIdList = (int*)palloc0(sizeof(int) * attrNum);
|
|
|
|
double numericExpandRatio = 0;
|
|
double numericSrcDataSize = 0;
|
|
double numericDataSize = 0;
|
|
|
|
for (int i = 0; i < attrNum; i++) {
|
|
colIdx[i] = attrs[i]->attnum;
|
|
slotIdList[i] = CACHE_BLOCK_INVALID_IDX;
|
|
}
|
|
|
|
cstoreScanDesc = CStoreBeginScan(onerel, attrNum, colIdx, NULL, false);
|
|
CStore* cstore = cstoreScanDesc->m_CStore;
|
|
uint32 maxCuId = cstore->GetMaxCUID(onerel->rd_rel->relcudescrelid, onerel->rd_att, SnapshotNow);
|
|
|
|
/*do not fetch the last CuID.*/
|
|
if (maxCuId > FirstCUID + 1) {
|
|
targetblock = FirstCUID + 1 + (gs_random() % (maxCuId - FirstCUID - 1));
|
|
} else {
|
|
targetblock = FirstCUID + 1;
|
|
}
|
|
|
|
/*sample the first CU of each column, and calculate the compression ratio of this table.*/
|
|
for (int col = 0; col < attrNum; col++) {
|
|
// skip dropped column
|
|
if (attrs[col]->attisdropped) {
|
|
continue;
|
|
}
|
|
|
|
bool found = cstore->GetCUDesc(col, targetblock, &cuDesc, SnapshotNow);
|
|
if (found && cuDesc.cu_size != 0) {
|
|
cuPtr = cstore->GetCUData(&cuDesc, col, attrs[col]->attlen, slotIdList[col]);
|
|
if ((cuPtr->m_infoMode & CU_IntLikeCompressed) && ATT_IS_NUMERIC_TYPE(cuPtr->m_atttypid)) {
|
|
numericExpandRatio = 1.5; /* default expand ratio */
|
|
numericDataSize = 0;
|
|
numericSrcDataSize = 0;
|
|
for (int rowId = 0; rowId < cuDesc.row_count; rowId++) {
|
|
if (cuPtr->IsNull(rowId)) {
|
|
continue;
|
|
}
|
|
char* srcData = cuPtr->m_srcData + cuPtr->m_offset[rowId];
|
|
Numeric nx = DatumGetNumeric(CStringGetDatum(srcData));
|
|
if (NUMERIC_IS_BI(nx)) {
|
|
Numeric srcNx = makeNumericNormal(nx);
|
|
numericSrcDataSize += VARSIZE_ANY(srcNx);
|
|
numericDataSize += cuPtr->m_offset[rowId + 1] - cuPtr->m_offset[rowId];
|
|
pfree_ext(srcNx);
|
|
}
|
|
}
|
|
if (numericSrcDataSize != 0 && numericDataSize != 0) {
|
|
numericExpandRatio = numericDataSize / numericSrcDataSize;
|
|
}
|
|
|
|
total_source_size += (double)(cuPtr->m_srcBufSize / numericExpandRatio);
|
|
total_cu_size += (double)cuPtr->m_cuSize;
|
|
} else {
|
|
total_source_size += (double)cuPtr->m_srcDataSize;
|
|
total_cu_size += (double)(cuPtr->m_cuSize - cuPtr->GetCUHeaderSize());
|
|
}
|
|
|
|
if (IsValidCacheSlotID(slotIdList[col])) {
|
|
CUCache->UnPinDataBlock(slotIdList[col]);
|
|
}
|
|
}
|
|
}
|
|
|
|
pfree_ext(colIdx);
|
|
pfree_ext(slotIdList);
|
|
CStoreEndScan(cstoreScanDesc);
|
|
|
|
return (total_cu_size > 0) ? (total_source_size / total_cu_size) : 0;
|
|
}
|
|
|
|
/*
|
|
* @Description: calculate the compress ratio of the column-partition relation.
|
|
* @in onerel: the relation.
|
|
* @return: the source size of this relation.
|
|
*/
|
|
static double calculate_parttable_compress_ratio(Relation onerel)
|
|
{
|
|
double compress_ratio = 0;
|
|
double sample_ratio = 0;
|
|
int partition_num = 0;
|
|
|
|
List* partitions = NIL;
|
|
ListCell* cell = NULL;
|
|
Partition partition = NULL;
|
|
Relation partRel = NULL;
|
|
|
|
/*fetch the compressed ratio of each partition, and get the average value.*/
|
|
partitions = relationGetPartitionList(onerel, AccessShareLock);
|
|
foreach (cell, partitions) {
|
|
partition = (Partition)lfirst(cell);
|
|
partRel = partitionGetRelation(onerel, partition);
|
|
sample_ratio = calculate_coltable_compress_ratio(partRel);
|
|
if (0 != sample_ratio) {
|
|
compress_ratio += sample_ratio;
|
|
partition_num++;
|
|
}
|
|
|
|
releaseDummyRelation(&partRel);
|
|
}
|
|
|
|
releasePartitionList(onerel, &partitions, AccessShareLock);
|
|
|
|
return (partition_num > 0) ? (compress_ratio / partition_num) : 0;
|
|
}
|
|
|
|
Datum pg_database_size_oid(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid dbOid = PG_GETARG_OID(0);
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if ((IS_PGXC_COORDINATOR && !IsConnFromCoord()))
|
|
PG_RETURN_DATUM(pgxc_database_size(dbOid));
|
|
#endif
|
|
|
|
/* Normally, check the validation of dbOid */
|
|
(void)get_and_check_db_name(dbOid, true);
|
|
size = calculate_database_size(dbOid);
|
|
|
|
if (size == 0)
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_database_size_name(PG_FUNCTION_ARGS)
|
|
{
|
|
Name dbName = PG_GETARG_NAME(0);
|
|
Oid dbOid = get_database_oid(NameStr(*dbName), false);
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
|
|
PG_RETURN_DATUM(pgxc_database_size(dbOid));
|
|
#endif
|
|
|
|
size = calculate_database_size(dbOid);
|
|
|
|
if (size == 0)
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
typedef struct SampleTuplesInfo {
|
|
Oid relid;
|
|
double tuples;
|
|
} SampleTuplesInfo;
|
|
|
|
static int compareRelationTuples(const void* a, const void* b, void* arg)
|
|
{
|
|
const SampleTuplesInfo* aa = (const SampleTuplesInfo*)a;
|
|
const SampleTuplesInfo* bb = (const SampleTuplesInfo*)b;
|
|
if (aa->tuples > bb->tuples)
|
|
return -1;
|
|
else if (aa->tuples < bb->tuples)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @Description: calculate the source size of database.
|
|
* @return: the size of database on one DN.
|
|
*/
|
|
static double calculate_source_database_size()
|
|
{
|
|
Relation class_rel;
|
|
Relation sample_rel;
|
|
SysScanDesc scan = NULL;
|
|
HeapTuple tuple;
|
|
Form_pg_class tuple_class;
|
|
TupleDesc pg_class_tuple_desc;
|
|
|
|
double row_tuples = 0;
|
|
double col_tuples = 0;
|
|
double sample_ratio = 0;
|
|
int64 sample_relation_size = 0;
|
|
double sample_relations_total_size = 0;
|
|
/*we think the compress ratio less than 0.5 is abnormal*/
|
|
const double abnormal_compress_ratio = 0.5;
|
|
double compress_ratio = 0;
|
|
double compress_ratio_weight = 0;
|
|
|
|
int64 size = 0;
|
|
int64 dbsize = 0;
|
|
int idx = 0;
|
|
|
|
/*fetch the most 128 relations to calculate compressed ratio.*/
|
|
int sample_relation_num = 128;
|
|
const int tmp_num = 1024;
|
|
|
|
SampleTuplesInfo* tupleInfo = (SampleTuplesInfo*)palloc0(tmp_num * sizeof(SampleTuplesInfo));
|
|
|
|
/*scan the pg_class to fetch the reltuples of every relation.*/
|
|
class_rel = heap_open(RelationRelationId, AccessShareLock);
|
|
pg_class_tuple_desc = class_rel->rd_att;
|
|
scan = systable_beginscan(class_rel, InvalidOid, false, NULL, 0, NULL);
|
|
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
|
|
tuple_class = (Form_pg_class)GETSTRUCT(tuple);
|
|
Oid relid = HeapTupleHeaderGetOid(tuple->t_data);
|
|
|
|
/* skip the table with zero retuples.*/
|
|
if (0 == tuple_class->reltuples) {
|
|
continue;
|
|
}
|
|
|
|
/*process the column tables.*/
|
|
if ((tuple_class->relkind != RELKIND_INDEX && tuple_class->relkind != RELKIND_GLOBAL_INDEX) &&
|
|
CheckRelOrientationByPgClassTuple(tuple, pg_class_tuple_desc, ORIENTATION_COLUMN)) {
|
|
col_tuples += tuple_class->reltuples;
|
|
if (idx < tmp_num) {
|
|
tupleInfo[idx].relid = relid;
|
|
tupleInfo[idx].tuples = tuple_class->reltuples;
|
|
idx++;
|
|
} else {
|
|
/*sort the tables with reltuples.*/
|
|
qsort_arg(tupleInfo, tmp_num, sizeof(SampleTuplesInfo), compareRelationTuples, NULL);
|
|
idx = sample_relation_num;
|
|
}
|
|
} else if (tuple_class->relkind == RELKIND_RELATION &&
|
|
CheckRelOrientationByPgClassTuple(tuple, pg_class_tuple_desc, ORIENTATION_ORC)) {
|
|
continue;
|
|
}
|
|
/*skip the foreign tables.*/
|
|
else if (tuple_class->relkind == RELKIND_FOREIGN_TABLE || tuple_class->relkind == RELKIND_STREAM) {
|
|
continue;
|
|
}
|
|
/*other tables are processed as row tables.*/
|
|
else {
|
|
row_tuples += tuple_class->reltuples;
|
|
}
|
|
}
|
|
systable_endscan(scan);
|
|
heap_close(class_rel, AccessShareLock);
|
|
|
|
if (idx != sample_relation_num) {
|
|
qsort_arg(tupleInfo, idx, sizeof(SampleTuplesInfo), compareRelationTuples, NULL);
|
|
}
|
|
sample_relation_num = (idx > sample_relation_num) ? sample_relation_num : idx;
|
|
|
|
/*calculate the compress ratio of each sample column table.*/
|
|
for (int i = 0; i < sample_relation_num; i++) {
|
|
sample_rel = try_relation_open(tupleInfo[i].relid, AccessShareLock);
|
|
if (NULL == sample_rel) {
|
|
continue;
|
|
}
|
|
|
|
/*non-partition column tables*/
|
|
if (!RelationIsPartitioned(sample_rel)) {
|
|
sample_ratio = calculate_coltable_compress_ratio(sample_rel);
|
|
if (sample_ratio < abnormal_compress_ratio) {
|
|
relation_close(sample_rel, AccessShareLock);
|
|
continue;
|
|
}
|
|
/*calculate the weight average ratio.*/
|
|
sample_relation_size = CalculateCStoreRelationSize(sample_rel, 0);
|
|
compress_ratio_weight += sample_ratio * sample_relation_size;
|
|
sample_relations_total_size += sample_relation_size;
|
|
}
|
|
/*partition column tables*/
|
|
else {
|
|
sample_ratio = calculate_parttable_compress_ratio(sample_rel);
|
|
if (sample_ratio < abnormal_compress_ratio) {
|
|
relation_close(sample_rel, AccessShareLock);
|
|
continue;
|
|
}
|
|
sample_relation_size = CalculateCStorePartitionedRelationSize(sample_rel, 0);
|
|
compress_ratio_weight += sample_ratio * sample_relation_size;
|
|
sample_relations_total_size += sample_relation_size;
|
|
}
|
|
|
|
relation_close(sample_rel, AccessShareLock);
|
|
}
|
|
|
|
compress_ratio = (compress_ratio_weight / sample_relations_total_size) > 1
|
|
? (compress_ratio_weight / sample_relations_total_size)
|
|
: 1;
|
|
|
|
/*calculate the size of current database.*/
|
|
dbsize = calculate_database_size(u_sess->proc_cxt.MyDatabaseId);
|
|
|
|
if (0 == (col_tuples + row_tuples)) {
|
|
size = 0;
|
|
ereport(WARNING,
|
|
(errmsg("The reltuples in pg_class is equal to zero."),
|
|
errhint("Do analyze for the current database first.")));
|
|
|
|
} else {
|
|
size = dbsize + dbsize * (col_tuples / (col_tuples + row_tuples)) * (compress_ratio - 1);
|
|
}
|
|
|
|
pfree_ext(tupleInfo);
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* @Description: the entry of this function.
|
|
* @return: the size of database on one DN or all DNs.
|
|
*/
|
|
Datum get_db_source_datasize(PG_FUNCTION_ARGS)
|
|
{
|
|
double size = 0;
|
|
|
|
#ifdef PGXC
|
|
/* the size of database on all DNs*/
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
|
|
PG_RETURN_DATUM(pgxc_source_database_size());
|
|
#endif
|
|
|
|
size = calculate_source_database_size();
|
|
|
|
if (size == 0)
|
|
PG_RETURN_NULL();
|
|
/* the size of database on one DN*/
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* Calculate total size of tablespace. Returns -1 if the tablespace directory
|
|
* cannot be found.
|
|
*/
|
|
static int64 calculate_tablespace_size(Oid tblspcOid)
|
|
{
|
|
char tblspcPath[MAXPGPATH] = {'\0'};
|
|
char pathname[MAXPGPATH] = {'\0'};
|
|
int64 totalsize = 0;
|
|
DIR* dirdesc = NULL;
|
|
struct dirent* direntry = NULL;
|
|
AclResult aclresult;
|
|
errno_t rc = EOK;
|
|
|
|
/*
|
|
* User must have CREATE privilege for target tablespace, either
|
|
* explicitly granted or implicitly because it is default for current
|
|
* database.
|
|
*/
|
|
if (tblspcOid != u_sess->proc_cxt.MyDatabaseTableSpace) {
|
|
aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tblspcOid));
|
|
}
|
|
|
|
if (IS_PGXC_DATANODE && IsConnFromApp() && IsSpecifiedTblspc(tblspcOid, FILESYSTEM_HDFS)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OBJECT_IN_USE),
|
|
errmsg("It is unsupported to calculate size of DFS tablespace \"%s\" on data node.",
|
|
get_tablespace_name(tblspcOid)),
|
|
errdetail("Please calculate size of DFS tablespace \"%s\" on coordinator node.",
|
|
get_tablespace_name(tblspcOid))));
|
|
}
|
|
if (tblspcOid == DEFAULTTABLESPACE_OID)
|
|
rc = snprintf_s(tblspcPath, MAXPGPATH, MAXPGPATH - 1, "base");
|
|
|
|
else if (tblspcOid == GLOBALTABLESPACE_OID)
|
|
rc = snprintf_s(tblspcPath, MAXPGPATH, MAXPGPATH - 1, "global");
|
|
else
|
|
#ifdef PGXC
|
|
/* Postgres-XC tablespaces include node name in path */
|
|
rc = snprintf_s(tblspcPath,
|
|
MAXPGPATH,
|
|
MAXPGPATH - 1,
|
|
"pg_tblspc/%u/%s_%s",
|
|
tblspcOid,
|
|
TABLESPACE_VERSION_DIRECTORY,
|
|
g_instance.attr.attr_common.PGXCNodeName);
|
|
#else
|
|
rc = snprintf_s(
|
|
tblspcPath, MAXPGPATH, MAXPGPATH - 1, "pg_tblspc/%u/%s", tblspcOid, TABLESPACE_VERSION_DIRECTORY);
|
|
#endif
|
|
securec_check_ss(rc, "\0", "\0");
|
|
dirdesc = AllocateDir(tblspcPath);
|
|
|
|
if (NULL == dirdesc)
|
|
return -1;
|
|
|
|
while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL) {
|
|
struct stat fst;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
if (unlikely(strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0))
|
|
continue;
|
|
|
|
rc = snprintf_s(pathname, MAXPGPATH, MAXPGPATH - 1, "%s/%s", tblspcPath, direntry->d_name);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
|
|
if (unlikely(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;
|
|
}
|
|
|
|
uint64 pg_cal_tablespace_size_oid(Oid tblspcOid)
|
|
{
|
|
return (uint64)(calculate_tablespace_size(tblspcOid));
|
|
}
|
|
|
|
Datum pg_tablespace_size_oid(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid tblspcOid = PG_GETARG_OID(0);
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
|
|
PG_RETURN_DATUM(pgxc_tablespace_size(tblspcOid));
|
|
#endif
|
|
|
|
size = calculate_tablespace_size(tblspcOid);
|
|
|
|
if (size < 0)
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_tablespace_size_name(PG_FUNCTION_ARGS)
|
|
{
|
|
Name tblspcName = PG_GETARG_NAME(0);
|
|
Oid tblspcOid = get_tablespace_oid(NameStr(*tblspcName), false);
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
|
|
PG_RETURN_DATUM(pgxc_tablespace_size(tblspcOid));
|
|
#endif
|
|
|
|
size = calculate_tablespace_size(tblspcOid);
|
|
|
|
if (size < 0)
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* calculate size of (one fork of) a relation
|
|
*
|
|
* Note: we can safely apply this to temp tables of other sessions, so there
|
|
* is no check here or at the call sites for that.
|
|
*/
|
|
int64 calculate_relation_size(RelFileNode* rfn, BackendId backend, ForkNumber forknum)
|
|
{
|
|
int64 totalsize = 0;
|
|
char* relationpath = NULL;
|
|
char pathname[MAXPGPATH] = {'\0'};
|
|
unsigned int segcount = 0;
|
|
errno_t rc = EOK;
|
|
|
|
relationpath = relpathbackend(*rfn, backend, forknum);
|
|
|
|
for (segcount = 0;; segcount++) {
|
|
struct stat fst;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
if (segcount == 0)
|
|
rc = snprintf_s(pathname, MAXPGPATH, MAXPGPATH - 1, "%s", relationpath);
|
|
else
|
|
rc = snprintf_s(pathname, MAXPGPATH, MAXPGPATH - 1, "%s.%u", relationpath, segcount);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
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;
|
|
}
|
|
|
|
pfree_ext(relationpath);
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/*
|
|
* calculate size of a bucket relation directory
|
|
*
|
|
* Note: we can safely apply this to temp tables of other sessions, so there
|
|
* is no check here or at the call sites for that.
|
|
*/
|
|
int64 calculate_relation_bucket_dir_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum)
|
|
{
|
|
int64 total_size = 0;
|
|
char *bucket_dir = NULL;
|
|
struct stat path_st;
|
|
char pathbuf[MAXPGPATH];
|
|
|
|
bucket_dir = relpathbackend(*rfn, backend, forknum);
|
|
if (stat(bucket_dir, &path_st) != 0) {
|
|
if (errno == ENOENT) {
|
|
pfree_ext(bucket_dir);
|
|
return total_size;
|
|
} else {
|
|
ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat bucket dir \"%s\": %m", bucket_dir)));
|
|
}
|
|
}
|
|
|
|
if (!S_ISDIR(path_st.st_mode)) {
|
|
ereport(ERROR, (errcode_for_file_access(), errmsg("rel path is not a dir \"%s\": %m", bucket_dir)));
|
|
}
|
|
|
|
char **filenames = pgfnames(bucket_dir);
|
|
if (filenames == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
for (char **filename = filenames; *filename != NULL; filename++) {
|
|
errno_t rc = snprintf_s(pathbuf, MAXPGPATH, MAXPGPATH - 1, "%s/%s", bucket_dir, *filename);
|
|
if (rc == -1) {
|
|
ereport(WARNING, (errmsg("pathbuf is not enough. bucket dir is %s, and file name is %s.\n",
|
|
bucket_dir, *filename)));
|
|
continue;
|
|
}
|
|
|
|
if (stat(pathbuf, &path_st) != 0 && errno != ENOENT) {
|
|
ereport(ERROR, (errcode_for_file_access(),
|
|
errmsg("could not stat file under bucket dir \"%s\": %m", pathbuf)));
|
|
}
|
|
|
|
if (S_ISDIR(path_st.st_mode)) {
|
|
ereport(WARNING, (errmsg("Bucket dir should contain sub dir \"%s\": %m", pathbuf)));
|
|
continue;
|
|
}
|
|
total_size += path_st.st_size;
|
|
}
|
|
|
|
pgfnames_cleanup(filenames);
|
|
|
|
pfree_ext(bucket_dir);
|
|
|
|
return total_size;
|
|
}
|
|
|
|
#ifdef ENABLE_MOT
|
|
uint64 CalculateMotRelationSize(Relation rel, Oid idxOid)
|
|
{
|
|
uint64 totalSize = 0;
|
|
FdwRoutine *fdwroutine = GetFdwRoutineForRelation(rel, false);
|
|
if (fdwroutine != NULL) {
|
|
totalSize = fdwroutine->GetForeignRelationMemSize(RelationGetRelid(rel), idxOid);
|
|
}
|
|
|
|
return totalSize;
|
|
}
|
|
#endif
|
|
|
|
int64 CalculateCStoreRelationSize(Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 totalsize = 0;
|
|
int64 size = 0;
|
|
uint64 segcount = 0;
|
|
char pathname[MAXPGPATH] = {'\0'};
|
|
|
|
if (forknum == MAIN_FORKNUM) {
|
|
/*
|
|
* Calcuate date files.
|
|
*/
|
|
if (RelationIsDfsStore(rel)) {
|
|
if (IS_PGXC_DATANODE && IsConnFromApp()) {
|
|
/*
|
|
* Calcuate data files size for orc on hdfs.
|
|
*/
|
|
size = getDFSRelSize(rel);
|
|
totalsize += size;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < RelationGetDescr(rel)->natts; i++) {
|
|
totalsize += calculate_relation_size(
|
|
&rel->rd_node, rel->rd_backend, ColumnId2ColForkNum(rel->rd_att->attrs[i]->attnum));
|
|
CFileNode tmpNode(rel->rd_node, rel->rd_att->attrs[i]->attnum, MAIN_FORKNUM);
|
|
CUStorage custore(tmpNode);
|
|
for (segcount = 0;; segcount++) {
|
|
struct stat fst;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
custore.GetFileName(pathname, MAXPGPATH, 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;
|
|
}
|
|
custore.Destroy();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add delta table's size
|
|
Relation deltaRel = try_relation_open(rel->rd_rel->reldeltarelid, AccessShareLock);
|
|
if (deltaRel != NULL) {
|
|
totalsize += calculate_relation_size(&(deltaRel->rd_node), deltaRel->rd_backend, forknum);
|
|
|
|
// Add CUDesc Toast table's size only when forkno is MAIN forkno.
|
|
if (MAIN_FORKNUM == forknum && OidIsValid(deltaRel->rd_rel->reltoastrelid)) {
|
|
totalsize += calculate_toast_table_size(deltaRel->rd_rel->reltoastrelid);
|
|
}
|
|
|
|
relation_close(deltaRel, AccessShareLock);
|
|
}
|
|
|
|
// Add CUDesc table's size
|
|
Relation cudescRel = try_relation_open(rel->rd_rel->relcudescrelid, AccessShareLock);
|
|
if (cudescRel != NULL) {
|
|
totalsize += calculate_relation_size(&(cudescRel->rd_node), cudescRel->rd_backend, forknum);
|
|
|
|
// Add CUDesc Toast table's size only when forkno is MAIN forkno.
|
|
if (MAIN_FORKNUM == forknum && OidIsValid(cudescRel->rd_rel->reltoastrelid)) {
|
|
totalsize += calculate_toast_table_size(cudescRel->rd_rel->reltoastrelid);
|
|
}
|
|
|
|
// Add CUDesc Index's size
|
|
Relation cudescIdx = try_relation_open(cudescRel->rd_rel->relcudescidx, AccessShareLock);
|
|
if (cudescIdx != NULL) {
|
|
totalsize += calculate_relation_size(&(cudescIdx->rd_node), cudescIdx->rd_backend, forknum);
|
|
// ignore toast relation size because CUDesc Index
|
|
// doesn't have any toast sub-relation.
|
|
relation_close(cudescIdx, AccessShareLock);
|
|
}
|
|
|
|
relation_close(cudescRel, AccessShareLock);
|
|
}
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
int64 CalculateCStorePartitionedRelationSize(Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 size = 0;
|
|
|
|
List* partitions = NIL;
|
|
ListCell* cell = NULL;
|
|
Partition partition = NULL;
|
|
Relation partRel = NULL;
|
|
|
|
partitions = relationGetPartitionList(rel, AccessShareLock);
|
|
|
|
foreach (cell, partitions) {
|
|
partition = (Partition)lfirst(cell);
|
|
partRel = partitionGetRelation(rel, partition);
|
|
size += CalculateCStoreRelationSize(partRel, forknum);
|
|
|
|
releaseDummyRelation(&partRel);
|
|
}
|
|
|
|
releasePartitionList(rel, &partitions, AccessShareLock);
|
|
|
|
return size;
|
|
}
|
|
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
/*
|
|
* @description: Calcute timeseries cudesc relation size.
|
|
* @param {rel}: partition relation
|
|
* @param {forknum} : forknum of file name
|
|
* @return {int64} : relation size
|
|
*/
|
|
int64 CalculateTsCudescRelationSize(const Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 totalsize = 0;
|
|
// Add CUDesc table's size
|
|
List* cudesc_oids = search_cudesc(rel->rd_id, false);
|
|
if (list_length(cudesc_oids) == 0) {
|
|
list_free_ext(cudesc_oids);
|
|
return totalsize;
|
|
}
|
|
ListCell *cell = NULL;
|
|
foreach(cell, cudesc_oids) {
|
|
Oid cudesc_oid = lfirst_oid(cell);
|
|
Relation cudescRel = try_relation_open(cudesc_oid, AccessShareLock);
|
|
if (cudescRel != NULL) {
|
|
totalsize += calculate_relation_size(&(cudescRel->rd_node), cudescRel->rd_backend, forknum);
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// Add TsCudesc Toast table's size only when forkno is MAIN forkno
|
|
if (MAIN_FORKNUM == forknum && OidIsValid(cudescRel->rd_rel->reltoastrelid)) {
|
|
totalsize += calculate_toast_table_size(cudescRel->rd_rel->reltoastrelid);
|
|
}
|
|
|
|
// Add TsCUDesc Index's size
|
|
Relation cudescIdx = try_relation_open(cudescRel->rd_rel->relcudescidx, AccessShareLock);
|
|
if (cudescIdx != NULL) {
|
|
totalsize += calculate_relation_size(&(cudescIdx->rd_node), cudescIdx->rd_backend, forknum);
|
|
// ignore toast relation size because CUDesc Index
|
|
// doesn't have any toast sub-relation.
|
|
relation_close(cudescIdx, AccessShareLock);
|
|
}
|
|
relation_close(cudescRel, AccessShareLock);
|
|
}
|
|
list_free_ext(cudesc_oids);
|
|
return totalsize;
|
|
}
|
|
|
|
/*
|
|
* @description: Calcute timeseries tag relation size.
|
|
* @param {rel}: partition relation
|
|
* @param {forknum} : forknum of file name
|
|
* @return {int64} : relation size
|
|
*/
|
|
int64 CalculateTsTagRelationSize(const Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 totalsize = 0;
|
|
|
|
Oid tstag_oid = get_tag_relid(RelationGetRelationName(rel), rel->rd_rel->relnamespace);
|
|
Relation tstag_rel = try_relation_open(tstag_oid, AccessShareLock);
|
|
if (tstag_rel != NULL) {
|
|
totalsize += calculate_relation_size(&(tstag_rel->rd_node), tstag_rel->rd_backend, forknum);
|
|
// Add TsTag Toast table's size only when forkno is MAIN forkno
|
|
if (MAIN_FORKNUM == forknum && OidIsValid(tstag_rel->rd_rel->reltoastrelid)) {
|
|
totalsize += calculate_toast_table_size(tstag_rel->rd_rel->reltoastrelid);
|
|
}
|
|
relation_close(tstag_rel, AccessShareLock);
|
|
|
|
// Add TsTag Index's size(todo, add more index)
|
|
Oid tstag_idx_oid = get_tag_index_relid(RelationGetRelationName(tstag_rel));
|
|
Relation tstag_idx_rel = try_relation_open(tstag_idx_oid, AccessShareLock);
|
|
if (tstag_idx_rel != NULL) {
|
|
totalsize += calculate_relation_size(&(tstag_idx_rel->rd_node), tstag_idx_rel->rd_backend, forknum);
|
|
relation_close(tstag_idx_rel, AccessShareLock);
|
|
}
|
|
}
|
|
return totalsize;
|
|
}
|
|
|
|
/*
|
|
* @description: Calcute timeseries column file size. Relation size includs:
|
|
* 1.pg_tags_*** relation size
|
|
* 2.pg_cudesc_*** relation size
|
|
* 4.column file size: 16395(partition_oid)_c2002(partid).0
|
|
* @param {rel}: partition relation
|
|
* @param {forknum} : forknum of file name
|
|
* @return {int64} : relation size
|
|
*/
|
|
int64 CalculateTStoreRelationSize(Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 totalsize = 0;
|
|
|
|
// Add delta table's size
|
|
|
|
int64 file_size = CalculateTsCudescRelationSize(rel, forknum);
|
|
if (file_size == 0) {
|
|
// No part exist in this partition
|
|
return totalsize;
|
|
}
|
|
totalsize += file_size;
|
|
|
|
uint64 segcount = 0;
|
|
char pathname[MAXPGPATH];
|
|
errno_t rc = memset_s(pathname, MAXPGPATH, 0, MAXPGPATH);
|
|
securec_check(rc, "", "");
|
|
if (forknum == MAIN_FORKNUM) {
|
|
// Calcuate date files.
|
|
for (uint16 partid = TsConf::FIRST_PARTID; partid < TsConf::MAX_PART_NUM; partid++) {
|
|
/*
|
|
* In timeseries relation, froknum is partid when calculate ts column file size because:
|
|
* Timeseries column name is like : 16391_C2002.0, 16391 is partition oid, 2002 is forknum.
|
|
*/
|
|
int64 col_file_size = calculate_relation_size(&rel->rd_node, rel->rd_backend, ColumnId2ColForkNum(partid));
|
|
totalsize += col_file_size;
|
|
CFileNode tmpNode(rel->rd_node, partid, MAIN_FORKNUM);
|
|
CUStorage custore(tmpNode);
|
|
for (segcount = 0;; segcount++) {
|
|
struct stat fst;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
custore.GetFileName(pathname, MAXPGPATH, segcount);
|
|
|
|
if (stat(pathname, &fst) < 0) {
|
|
if (errno == ENOENT)
|
|
break;
|
|
else
|
|
ereport(
|
|
LOG, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", pathname)));
|
|
}
|
|
totalsize += fst.st_size;
|
|
}
|
|
custore.Destroy();
|
|
}
|
|
}
|
|
|
|
return totalsize;
|
|
}
|
|
|
|
/*
|
|
* @description: Calcute timeseries column file size by partition.
|
|
* @param {rel}: relation
|
|
* @param {forknum}
|
|
* @return {int64} : relation size
|
|
*/
|
|
int64 CalculateTStorePartitionedRelationSize(Relation rel, ForkNumber forknum)
|
|
{
|
|
int64 size = 0;
|
|
|
|
List* partitions = NIL;
|
|
ListCell* cell = NULL;
|
|
Partition partition = NULL;
|
|
Relation partRel = NULL;
|
|
|
|
size += CalculateTsTagRelationSize(rel, forknum);
|
|
|
|
partitions = relationGetPartitionList(rel, AccessShareLock);
|
|
|
|
foreach (cell, partitions) {
|
|
partition = (Partition)lfirst(cell);
|
|
partRel = partitionGetRelation(rel, partition);
|
|
size += CalculateTStoreRelationSize(partRel, forknum);
|
|
|
|
releaseDummyRelation(&partRel);
|
|
}
|
|
|
|
releasePartitionList(rel, &partitions, AccessShareLock);
|
|
|
|
return size;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Calculate total on-disk size of a TOAST relation, including its index.
|
|
* Must not be applied to non-TOAST relations.
|
|
*/
|
|
static int64 calculate_toast_table_size(Oid toastrelid)
|
|
{
|
|
int64 size = 0;
|
|
Relation toastRel;
|
|
Relation toastIdxRel;
|
|
ForkNumber forkNum;
|
|
|
|
toastRel = relation_open(toastrelid, AccessShareLock);
|
|
|
|
/* toast heap size, including FSM and VM size */
|
|
for (int ifork = 0; ifork <= MAX_FORKNUM; ifork++) {
|
|
forkNum = (ForkNumber)ifork;
|
|
size += calculate_table_file_size(toastRel, false, forkNum);
|
|
}
|
|
|
|
/* toast index size, including FSM and VM size */
|
|
toastIdxRel = relation_open(toastRel->rd_rel->reltoastidxid, AccessShareLock);
|
|
for (int ifork = 0; ifork <= MAX_FORKNUM; ifork++) {
|
|
forkNum = (ForkNumber)ifork;
|
|
size += calculate_table_file_size(toastIdxRel, false, forkNum);
|
|
}
|
|
|
|
relation_close(toastIdxRel, AccessShareLock);
|
|
relation_close(toastRel, AccessShareLock);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int64 calculate_table_file_size(Relation rel, bool isCStore, int forkNumOption)
|
|
{
|
|
int64 size = 0;
|
|
ForkNumber forkNum = (ForkNumber)0;
|
|
|
|
if (RELATION_CREATE_BUCKET(rel)) {
|
|
Relation heapBucketRel = NULL;
|
|
oidvector* bucketlist = searchHashBucketByOid(rel->rd_bucketoid);
|
|
|
|
for (int i = 0; i < bucketlist->dim1; i++) {
|
|
heapBucketRel = bucketGetRelation(rel, NULL, bucketlist->values[i]);
|
|
if (forkNumOption == DEFAULT_FORKNUM) {
|
|
/*
|
|
* heap size, including FSM and VM
|
|
*/
|
|
for (int ifork = 0; ifork <= MAX_FORKNUM; ifork++) {
|
|
forkNum = (ForkNumber)ifork;
|
|
|
|
size += calculate_relation_size(&(heapBucketRel->rd_node), InvalidBackendId, forkNum);
|
|
}
|
|
} else {
|
|
size += calculate_relation_size(&(heapBucketRel->rd_node), InvalidBackendId, forkNumOption);
|
|
}
|
|
bucketCloseRelation(heapBucketRel);
|
|
}
|
|
/*
|
|
* Size of toast relation
|
|
*/
|
|
if (OidIsValid(rel->rd_rel->reltoastrelid)) {
|
|
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
|
|
}
|
|
} else {
|
|
if (forkNumOption == DEFAULT_FORKNUM) {
|
|
/*
|
|
* heap size, including FSM and VM
|
|
*/
|
|
for (int ifork = 0; ifork <= MAX_FORKNUM; ifork++) {
|
|
forkNum = (ForkNumber)ifork;
|
|
|
|
if (isCStore) {
|
|
size += CalculateCStoreRelationSize(rel, forkNum);
|
|
}
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
else if (RelationIsTsStore(rel)) {
|
|
size += CalculateTStoreRelationSize(rel, forkNum);
|
|
}
|
|
#endif
|
|
else {
|
|
size += calculate_relation_size(&(rel->rd_node), rel->rd_backend, forkNum);
|
|
}
|
|
}
|
|
/*
|
|
* Size of toast relation
|
|
*/
|
|
if (OidIsValid(rel->rd_rel->reltoastrelid)) {
|
|
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
|
|
}
|
|
} else {
|
|
if (isCStore) {
|
|
size += CalculateCStoreRelationSize(rel, forkNumOption);
|
|
} else if (RelationIsTsStore(rel)) {
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
size += CalculateTStoreRelationSize(rel, forkNumOption);
|
|
#endif
|
|
} else {
|
|
size += calculate_relation_size(&(rel->rd_node), rel->rd_backend, forkNumOption);
|
|
}
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Calculate total on-disk size of a given table,
|
|
* including FSM and VM, plus TOAST table if any.
|
|
* Indexes other than the TOAST table's index are not included.
|
|
*
|
|
* Note that this also behaves sanely if applied to an index or toast table;
|
|
* those won't have attached toast tables, but they can have multiple forks.
|
|
*/
|
|
static int64 calculate_table_size(Relation rel, int forkNumOption)
|
|
{
|
|
int64 size = 0;
|
|
|
|
if (!RelationIsIndex(rel)) {
|
|
#ifdef ENABLE_MOT
|
|
if (RelationIsForeignTable(rel) && RelationIsMOTTableByOid(RelationGetRelid(rel))) {
|
|
size = CalculateMotRelationSize(rel, InvalidOid);
|
|
} else if (!RelationIsPartitioned(rel)) {
|
|
#else
|
|
if (!RelationIsPartitioned(rel)) {
|
|
#endif
|
|
size = calculate_table_file_size(rel, RelationIsColStore(rel), forkNumOption);
|
|
} else {
|
|
List* partitions = NIL;
|
|
ListCell* cell = NULL;
|
|
Partition partition = NULL;
|
|
Relation partRel = NULL;
|
|
partitions = relationGetPartitionList(rel, AccessShareLock);
|
|
|
|
foreach (cell, partitions) {
|
|
partition = (Partition)lfirst(cell);
|
|
partRel = partitionGetRelation(rel, partition);
|
|
|
|
size += calculate_table_file_size(partRel, RelationIsColStore(rel), forkNumOption);
|
|
|
|
releaseDummyRelation(&partRel);
|
|
}
|
|
|
|
releasePartitionList(rel, &partitions, AccessShareLock);
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
if (RelationIsTsStore(rel)) {
|
|
size += CalculateTsTagRelationSize(rel, forkNumOption);
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
Relation baseRel = relation_open(rel->rd_index->indrelid, AccessShareLock);
|
|
bool bCstore = RelationIsColStore(baseRel) && (rel->rd_rel->relam == PSORT_AM_OID);
|
|
|
|
#ifdef ENABLE_MOT
|
|
if (RelationIsForeignTable(baseRel) && RelationIsMOTTableByOid(RelationGetRelid(baseRel))) {
|
|
size = CalculateMotRelationSize(baseRel, RelationGetRelid(rel));
|
|
} else if (!RelationIsPartitioned(rel)) {
|
|
#else
|
|
if (!RelationIsPartitioned(rel)) {
|
|
#endif
|
|
if (!bCstore)
|
|
size = calculate_table_file_size(rel, false, forkNumOption);
|
|
else {
|
|
Relation cstoreIdxRel = relation_open(rel->rd_rel->relcudescrelid, AccessShareLock);
|
|
if (cstoreIdxRel != NULL) {
|
|
size = calculate_table_file_size(cstoreIdxRel, true, forkNumOption);
|
|
relation_close(cstoreIdxRel, AccessShareLock);
|
|
}
|
|
}
|
|
} else {
|
|
List* partOids = NIL;
|
|
ListCell* cell = NULL;
|
|
Oid partOid = InvalidOid;
|
|
Oid partIndexOid = InvalidOid;
|
|
Partition partIndex = NULL;
|
|
Relation partIndexRel = NULL;
|
|
Relation cstorePartIndexRel = NULL;
|
|
|
|
partOids = relationGetPartitionOidList(baseRel);
|
|
|
|
foreach (cell, partOids) {
|
|
partOid = lfirst_oid(cell);
|
|
partIndexOid = getPartitionIndexOid(RelationGetRelid(rel), partOid);
|
|
partIndex = partitionOpen(rel, partIndexOid, AccessShareLock);
|
|
partIndexRel = partitionGetRelation(rel, partIndex);
|
|
|
|
if (bCstore) {
|
|
cstorePartIndexRel = relation_open(partIndexRel->rd_rel->relcudescrelid, AccessShareLock);
|
|
size += calculate_table_file_size(cstorePartIndexRel, true, forkNumOption);
|
|
relation_close(cstorePartIndexRel, AccessShareLock);
|
|
} else
|
|
size += calculate_table_file_size(partIndexRel, false, forkNumOption);
|
|
|
|
partitionClose(rel, partIndex, AccessShareLock);
|
|
releaseDummyRelation(&partIndexRel);
|
|
}
|
|
|
|
releasePartitionOidList(&partOids);
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
if (RelationIsTsStore(rel)) {
|
|
size += CalculateTsTagRelationSize(rel, forkNumOption);
|
|
}
|
|
#endif
|
|
}
|
|
relation_close(baseRel, AccessShareLock);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Calculate total on-disk size of all indexes attached to the given table.
|
|
*
|
|
* Can be applied safely to an index, but you'll just get zero.
|
|
*/
|
|
static int64 calculate_indexes_size(Relation rel)
|
|
{
|
|
int64 size = 0;
|
|
|
|
/*
|
|
* Aggregate all indexes on the given relation
|
|
*/
|
|
if (rel->rd_rel->relhasindex) {
|
|
List* index_oids = RelationGetIndexList(rel);
|
|
ListCell* cell = NULL;
|
|
|
|
foreach (cell, index_oids) {
|
|
Oid idxOid = lfirst_oid(cell);
|
|
Relation idxRel = NULL;
|
|
|
|
idxRel = relation_open(idxOid, AccessShareLock);
|
|
|
|
size += calculate_table_size(idxRel, DEFAULT_FORKNUM);
|
|
|
|
relation_close(idxRel, AccessShareLock);
|
|
}
|
|
|
|
list_free_ext(index_oids);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
Datum pg_relation_size(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relOid = PG_GETARG_OID(0);
|
|
text* forkName = PG_GETARG_TEXT_P(1);
|
|
Relation rel;
|
|
int64 size = 0;
|
|
|
|
#ifdef PGXC
|
|
if (COLLECT_FROM_DATANODES(relOid)) {
|
|
size = pgxc_exec_sizefunc(relOid, "pg_relation_size", text_to_cstring(forkName));
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
#endif /* PGXC */
|
|
|
|
rel = try_relation_open(relOid, AccessShareLock);
|
|
|
|
/*
|
|
* Before 9.2, we used to throw an error if the relation didn't exist, but
|
|
* that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
|
|
* less robust, because while we scan pg_class with an MVCC snapshot,
|
|
* someone else might drop the table. It's better to return NULL for
|
|
* already-dropped tables than throw an error and abort the whole query.
|
|
*/
|
|
if (rel == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
ForkNumber forknumber = forkname_to_number(text_to_cstring(forkName));
|
|
|
|
size = calculate_table_size(rel, forknumber);
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_table_size(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relOid = PG_GETARG_OID(0);
|
|
Relation rel = NULL;
|
|
int64 size;
|
|
|
|
/* acquire lock to prevent relation drop in other parallel */
|
|
rel = try_relation_open(relOid, AccessShareLock);
|
|
|
|
if (!RelationIsValid(rel)) {
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
#ifdef PGXC
|
|
if (COLLECT_FROM_DATANODES(relOid)) {
|
|
size = pgxc_exec_sizefunc(relOid, "pg_table_size", NULL);
|
|
} else {
|
|
#else
|
|
if (true) {
|
|
#endif
|
|
size = calculate_table_size(rel, DEFAULT_FORKNUM);
|
|
}
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
rel = NULL;
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_partition_size_oid(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid partTableOid = PG_GETARG_OID(0);
|
|
Oid partOid = PG_GETARG_OID(1);
|
|
int64 size = 0;
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && (GetRelationLocInfo((partTableOid)) != NULL)) {
|
|
PG_RETURN_INT64(pgxc_exec_partition_sizefunc(partTableOid, partOid, "pg_partition_size"));
|
|
}
|
|
#endif
|
|
|
|
size = calculate_partition_size(partTableOid, partOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_partition_size_name(PG_FUNCTION_ARGS)
|
|
{
|
|
char* partTableName = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
|
char* partName = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
|
Oid partTableOid = InvalidOid;
|
|
Oid partOid = InvalidOid;
|
|
List* names = NIL;
|
|
int64 size = 0;
|
|
|
|
names = stringToQualifiedNameList(partTableName);
|
|
partTableOid = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false);
|
|
|
|
partOid = partitionNameGetPartitionOid(
|
|
partTableOid, partName, PART_OBJ_TYPE_TABLE_PARTITION, NoLock, false, false, NULL, NULL, NoLock);
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && (GetRelationLocInfo((partTableOid)) != NULL)) {
|
|
PG_RETURN_INT64(pgxc_exec_partition_sizefunc(partTableOid, partOid, "pg_partition_size"));
|
|
}
|
|
#endif
|
|
|
|
size = calculate_partition_size(partTableOid, partOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
static int64 calculate_partition_size(Oid partTableOid, Oid partOid)
|
|
{
|
|
Relation partTableRel = NULL;
|
|
Partition partition = NULL;
|
|
Relation partRel = NULL;
|
|
int64 size = 0;
|
|
|
|
partTableRel = try_relation_open(partTableOid, AccessShareLock);
|
|
|
|
if (partTableRel == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
partition = partitionOpen(partTableRel, partOid, AccessShareLock);
|
|
partRel = partitionGetRelation(partTableRel, partition);
|
|
|
|
size = calculate_table_file_size(partRel, RelationIsColStore(partRel), DEFAULT_FORKNUM);
|
|
|
|
partitionClose(partTableRel, partition, AccessShareLock);
|
|
releaseDummyRelation(&partRel);
|
|
relation_close(partTableRel, AccessShareLock);
|
|
|
|
return size;
|
|
}
|
|
|
|
Datum pg_partition_indexes_size_oid(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid partTableOid = PG_GETARG_OID(0);
|
|
Oid partOid = PG_GETARG_OID(1);
|
|
int64 size = 0;
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && (GetRelationLocInfo((partTableOid)) != NULL)) {
|
|
PG_RETURN_INT64(pgxc_exec_partition_sizefunc(partTableOid, partOid, "pg_partition_indexes_size"));
|
|
}
|
|
#endif
|
|
|
|
size = calculate_partition_indexes_size(partTableOid, partOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
Datum pg_partition_indexes_size_name(PG_FUNCTION_ARGS)
|
|
{
|
|
char* partTableName = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
|
char* partName = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
|
Oid partTableOid = InvalidOid;
|
|
Oid partOid = InvalidOid;
|
|
List* names = NIL;
|
|
int64 size = 0;
|
|
|
|
names = stringToQualifiedNameList(partTableName);
|
|
partTableOid = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false);
|
|
|
|
partOid = partitionNameGetPartitionOid(
|
|
partTableOid, partName, PART_OBJ_TYPE_TABLE_PARTITION, NoLock, false, false, NULL, NULL, NoLock);
|
|
|
|
#ifdef PGXC
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && (GetRelationLocInfo((partTableOid)) != NULL)) {
|
|
PG_RETURN_INT64(pgxc_exec_partition_sizefunc(partTableOid, partOid, "pg_partition_indexes_size"));
|
|
}
|
|
#endif
|
|
|
|
size = calculate_partition_indexes_size(partTableOid, partOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
static int64 calculate_partition_indexes_size(Oid partTableOid, Oid partOid)
|
|
{
|
|
Relation partTableRel = NULL;
|
|
List* indexOids = NIL;
|
|
ListCell* cell = NULL;
|
|
int64 size = 0;
|
|
|
|
partTableRel = try_relation_open(partTableOid, AccessShareLock);
|
|
|
|
if (partTableRel == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
indexOids = RelationGetIndexList(partTableRel);
|
|
|
|
foreach (cell, indexOids) {
|
|
Oid indexOid = lfirst_oid(cell);
|
|
Relation indexRel = NULL;
|
|
Oid partIndexOid = InvalidOid;
|
|
Partition partIndex = NULL;
|
|
Relation partIndexRel = NULL;
|
|
|
|
indexRel = relation_open(indexOid, AccessShareLock);
|
|
partIndexOid = getPartitionIndexOid(indexOid, partOid);
|
|
partIndex = partitionOpen(indexRel, partIndexOid, AccessShareLock);
|
|
partIndexRel = partitionGetRelation(indexRel, partIndex);
|
|
|
|
size += calculate_table_size(partIndexRel, DEFAULT_FORKNUM);
|
|
|
|
partitionClose(indexRel, partIndex, AccessShareLock);
|
|
releaseDummyRelation(&partIndexRel);
|
|
relation_close(indexRel, AccessShareLock);
|
|
}
|
|
|
|
list_free_ext(indexOids);
|
|
relation_close(partTableRel, AccessShareLock);
|
|
|
|
return size;
|
|
}
|
|
|
|
Datum pg_indexes_size(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relOid = PG_GETARG_OID(0);
|
|
Relation rel;
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if (COLLECT_FROM_DATANODES(relOid))
|
|
PG_RETURN_INT64(pgxc_exec_sizefunc(relOid, "pg_indexes_size", NULL));
|
|
#endif /* PGXC */
|
|
|
|
rel = try_relation_open(relOid, AccessShareLock);
|
|
|
|
if (rel == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
size = calculate_indexes_size(rel);
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* Compute the on-disk size of all files for the relation,
|
|
* including heap data, index data, toast data, FSM, VM.
|
|
*/
|
|
static int64 calculate_total_relation_size(Relation rel)
|
|
{
|
|
int64 size;
|
|
|
|
/*
|
|
* Aggregate the table size, this includes size of the heap, toast and
|
|
* toast index with free space and visibility map
|
|
*/
|
|
size = calculate_table_size(rel, DEFAULT_FORKNUM);
|
|
|
|
/*
|
|
* Add size of all attached indexes as well
|
|
*/
|
|
size += calculate_indexes_size(rel);
|
|
|
|
return size;
|
|
}
|
|
|
|
Datum pg_total_relation_size(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relOid = PG_GETARG_OID(0);
|
|
Relation rel;
|
|
int64 size;
|
|
|
|
#ifdef PGXC
|
|
if (COLLECT_FROM_DATANODES(relOid))
|
|
PG_RETURN_INT64(pgxc_exec_sizefunc(relOid, "pg_total_relation_size", NULL));
|
|
#endif /* PGXC */
|
|
|
|
rel = try_relation_open(relOid, AccessShareLock);
|
|
|
|
if (rel == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
size = calculate_total_relation_size(rel);
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* formatting with size units
|
|
*/
|
|
Datum pg_size_pretty(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 size = PG_GETARG_INT64(0);
|
|
char buf[64];
|
|
const int64 limit = 10 * 1024;
|
|
int64 limit2 = limit * 2 - 1;
|
|
errno_t rc = EOK;
|
|
|
|
if (size < 0) {
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value should not be negative.")));
|
|
return (Datum)0;
|
|
}
|
|
if (size < limit)
|
|
rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, INT64_FORMAT " bytes", size);
|
|
else {
|
|
size >>= 9; /* keep one extra bit for rounding */
|
|
if (size < limit2)
|
|
rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, INT64_FORMAT " kB", (size + 1) / 2);
|
|
|
|
else {
|
|
size >>= 10;
|
|
if (size < limit2)
|
|
rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, INT64_FORMAT " MB", (size + 1) / 2);
|
|
else {
|
|
size >>= 10;
|
|
if (size < limit2)
|
|
rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, INT64_FORMAT " GB", (size + 1) / 2);
|
|
|
|
else {
|
|
size >>= 10;
|
|
rc = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, INT64_FORMAT " TB", (size + 1) / 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
securec_check_ss(rc, "\0", "\0");
|
|
PG_RETURN_TEXT_P(cstring_to_text(buf));
|
|
}
|
|
|
|
static char* numeric_to_cstring(Numeric n)
|
|
{
|
|
Datum d = NumericGetDatum(n);
|
|
|
|
return DatumGetCString(DirectFunctionCall1(numeric_out, d));
|
|
}
|
|
|
|
Numeric int64_to_numeric(int64 v)
|
|
{
|
|
Datum d = Int64GetDatum(v);
|
|
|
|
return DatumGetNumeric(DirectFunctionCall1(int8_numeric, d));
|
|
}
|
|
|
|
static bool numeric_is_less(Numeric a, Numeric b)
|
|
{
|
|
Datum da = NumericGetDatum(a);
|
|
Datum db = NumericGetDatum(b);
|
|
|
|
return DatumGetBool(DirectFunctionCall2(numeric_lt, da, db));
|
|
}
|
|
|
|
static Numeric numeric_plus_one_over_two(Numeric n)
|
|
{
|
|
Datum d = NumericGetDatum(n);
|
|
Datum one;
|
|
Datum two;
|
|
Datum result;
|
|
|
|
one = DirectFunctionCall1(int8_numeric, Int64GetDatum(1));
|
|
two = DirectFunctionCall1(int8_numeric, Int64GetDatum(2));
|
|
result = DirectFunctionCall2(numeric_add, d, one);
|
|
result = DirectFunctionCall2(numeric_div_trunc, result, two);
|
|
return DatumGetNumeric(result);
|
|
}
|
|
|
|
static Numeric numeric_shift_right(Numeric n, unsigned count)
|
|
{
|
|
Datum d = NumericGetDatum(n);
|
|
Datum divisor_int64;
|
|
Datum divisor_numeric;
|
|
Datum result;
|
|
|
|
divisor_int64 = Int64GetDatum((int64)((uint64)1 << count));
|
|
divisor_numeric = DirectFunctionCall1(int8_numeric, divisor_int64);
|
|
result = DirectFunctionCall2(numeric_div_trunc, d, divisor_numeric);
|
|
return DatumGetNumeric(result);
|
|
}
|
|
|
|
Datum pg_size_pretty_numeric(PG_FUNCTION_ARGS)
|
|
{
|
|
Numeric size = PG_GETARG_NUMERIC(0);
|
|
Numeric limit, limit2;
|
|
char *buf = NULL, *result = NULL;
|
|
errno_t rc = EOK;
|
|
int result_size;
|
|
|
|
if (numeric_is_less(size, int64_to_numeric(0))) {
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value should not be negative.")));
|
|
return (Datum)0;
|
|
}
|
|
limit = int64_to_numeric(10 * 1024);
|
|
limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
|
|
|
|
if (numeric_is_less(size, limit)) {
|
|
buf = numeric_to_cstring(size);
|
|
result_size = strlen(buf) + 7;
|
|
result = (char*)palloc(result_size);
|
|
rc = strcpy_s(result, result_size, buf);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strcat_s(result, result_size, " bytes");
|
|
securec_check(rc, "\0", "\0");
|
|
} else {
|
|
/* keep one extra bit for rounding */
|
|
/* size >>= 9 */
|
|
size = numeric_shift_right(size, 9);
|
|
|
|
if (numeric_is_less(size, limit2)) {
|
|
/* size = (size + 1) / 2 */
|
|
size = numeric_plus_one_over_two(size);
|
|
buf = numeric_to_cstring(size);
|
|
result_size = strlen(buf) + 4;
|
|
result = (char*)palloc(result_size);
|
|
rc = strcpy_s(result, result_size, buf);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strcat_s(result, result_size, " kB");
|
|
securec_check(rc, "\0", "\0");
|
|
} else {
|
|
/* size >>= 10 */
|
|
size = numeric_shift_right(size, 10);
|
|
if (numeric_is_less(size, limit2)) {
|
|
/* size = (size + 1) / 2 */
|
|
size = numeric_plus_one_over_two(size);
|
|
buf = numeric_to_cstring(size);
|
|
result_size = strlen(buf) + 4;
|
|
result = (char*)palloc(result_size);
|
|
rc = strcpy_s(result, result_size, buf);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strcat_s(result, result_size, " MB");
|
|
securec_check(rc, "\0", "\0");
|
|
} else {
|
|
/* size >>= 10 */
|
|
size = numeric_shift_right(size, 10);
|
|
|
|
if (numeric_is_less(size, limit2)) {
|
|
/* size = (size + 1) / 2 */
|
|
size = numeric_plus_one_over_two(size);
|
|
buf = numeric_to_cstring(size);
|
|
result_size = strlen(buf) + 4;
|
|
result = (char*)palloc(result_size);
|
|
rc = strcpy_s(result, result_size, buf);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strcat_s(result, result_size, " GB");
|
|
securec_check(rc, "\0", "\0");
|
|
} else {
|
|
/* size >>= 10 */
|
|
size = numeric_shift_right(size, 10);
|
|
/* size = (size + 1) / 2 */
|
|
size = numeric_plus_one_over_two(size);
|
|
buf = numeric_to_cstring(size);
|
|
result_size = strlen(buf) + 4;
|
|
result = (char*)palloc(result_size);
|
|
rc = strcpy_s(result, result_size, buf);
|
|
securec_check(rc, "\0", "\0");
|
|
rc = strcat_s(result, result_size, " TB");
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(result));
|
|
}
|
|
|
|
/*
|
|
* Get the filenode of a relation
|
|
*
|
|
* This is expected to be used in queries like
|
|
* SELECT pg_relation_filenode(oid) FROM pg_class;
|
|
* That leads to a couple of choices. We work from the pg_class row alone
|
|
* rather than actually opening each relation, for efficiency. We don't
|
|
* fail if we can't find the relation --- some rows might be visible in
|
|
* the query's MVCC snapshot but already dead according to SnapshotNow.
|
|
* (Note: we could avoid using the catcache, but there's little point
|
|
* because the relation mapper also works "in the now".) We also don't
|
|
* fail if the relation doesn't have storage. In all these cases it
|
|
* seems better to quietly return NULL.
|
|
*/
|
|
Datum pg_relation_filenode(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relid = PG_GETARG_OID(0);
|
|
Oid result;
|
|
HeapTuple tuple;
|
|
Form_pg_class relform;
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tuple))
|
|
PG_RETURN_NULL();
|
|
relform = (Form_pg_class)GETSTRUCT(tuple);
|
|
|
|
switch (relform->relkind) {
|
|
case RELKIND_RELATION:
|
|
case RELKIND_MATVIEW:
|
|
case RELKIND_INDEX:
|
|
case RELKIND_GLOBAL_INDEX:
|
|
case RELKIND_SEQUENCE:
|
|
case RELKIND_TOASTVALUE:
|
|
/* okay, these have storage */
|
|
if (relform->relfilenode)
|
|
result = relform->relfilenode;
|
|
else /* Consult the relation mapper */
|
|
result = RelationMapOidToFilenode(relid, relform->relisshared);
|
|
break;
|
|
|
|
default:
|
|
/* no storage, return NULL */
|
|
result = InvalidOid;
|
|
break;
|
|
}
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
if (!OidIsValid(result))
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_OID(result);
|
|
}
|
|
|
|
/*
|
|
* Get the relation via (reltablespace, relfilenode)
|
|
*
|
|
* This is expected to be used when somebody wants to match an individual file
|
|
* on the filesystem back to its table. Thats not trivially possible via
|
|
* pg_class because that doesn't contain the relfilenodes of shared and nailed
|
|
* tables.
|
|
*
|
|
* We don't fail but return NULL if we cannot find a mapping.
|
|
*
|
|
* Instead of knowing DEFAULTTABLESPACE_OID you can pass 0.
|
|
*/
|
|
Datum pg_filenode_relation(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid reltablespace = PG_GETARG_OID(0);
|
|
Oid relfilenode = PG_GETARG_OID(1);
|
|
Oid heaprel = InvalidOid;
|
|
|
|
heaprel = RelidByRelfilenodeCache(reltablespace, relfilenode);
|
|
if (!OidIsValid(heaprel)) {
|
|
heaprel = RelidByRelfilenode(reltablespace, relfilenode);
|
|
}
|
|
|
|
if (!OidIsValid(heaprel))
|
|
PG_RETURN_NULL();
|
|
else
|
|
PG_RETURN_OID(heaprel);
|
|
}
|
|
|
|
/*
|
|
* Get the filenode of a partition
|
|
* This is expected to be used in queries like
|
|
* SELECT pg_partition_filenode(oid);
|
|
*/
|
|
Datum pg_partition_filenode(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid partRelId = PG_GETARG_OID(0);
|
|
Oid result;
|
|
HeapTuple tuple;
|
|
Form_pg_partition partRelForm;
|
|
|
|
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partRelId));
|
|
if (!HeapTupleIsValid(tuple))
|
|
PG_RETURN_NULL();
|
|
|
|
partRelForm = (Form_pg_partition)GETSTRUCT(tuple);
|
|
switch (partRelForm->parttype) {
|
|
case PART_OBJ_TYPE_PARTED_TABLE:
|
|
case PART_OBJ_TYPE_TABLE_PARTITION:
|
|
case PART_OBJ_TYPE_INDEX_PARTITION:
|
|
case PART_OBJ_TYPE_TOAST_TABLE:
|
|
// okay, these have storage
|
|
if (partRelForm->relfilenode)
|
|
result = partRelForm->relfilenode;
|
|
else
|
|
result = RelationMapOidToFilenode(partRelId, false);
|
|
break;
|
|
|
|
default:
|
|
// no storage, return NULL
|
|
result = InvalidOid;
|
|
break;
|
|
}
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
if (!OidIsValid(result))
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_OID(result);
|
|
}
|
|
|
|
/*
|
|
* * Get the pathname (relative to $PGDATA) of a partition
|
|
* *
|
|
* */
|
|
Datum pg_partition_filepath(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid partRelId = PG_GETARG_OID(0);
|
|
HeapTuple tuple;
|
|
Form_pg_partition partRelForm;
|
|
RelFileNode rnode;
|
|
BackendId backend = InvalidBackendId;
|
|
char* path = NULL;
|
|
|
|
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partRelId));
|
|
if (!HeapTupleIsValid(tuple))
|
|
PG_RETURN_NULL();
|
|
partRelForm = (Form_pg_partition)GETSTRUCT(tuple);
|
|
|
|
switch (partRelForm->parttype) {
|
|
case PARTTYPE_PARTITIONED_RELATION:
|
|
case PARTTYPE_VALUE_PARTITIONED_RELATION:
|
|
rnode.spcNode = ConvertToRelfilenodeTblspcOid(partRelForm->reltablespace);
|
|
if (rnode.spcNode == GLOBALTABLESPACE_OID) {
|
|
rnode.dbNode = InvalidOid;
|
|
} else {
|
|
rnode.dbNode = u_sess->proc_cxt.MyDatabaseId;
|
|
}
|
|
if (partRelForm->relfilenode) {
|
|
rnode.relNode = partRelForm->relfilenode;
|
|
} else {
|
|
/* Consult the relation mapper */
|
|
rnode.relNode = RelationMapOidToFilenode(partRelId, false);
|
|
}
|
|
rnode.bucketNode = InvalidBktId;
|
|
break;
|
|
|
|
default:
|
|
/* no storage, return NULL */
|
|
rnode.relNode = InvalidOid;
|
|
/* some compilers generate warnings without these next two lines */
|
|
rnode.dbNode = InvalidOid;
|
|
rnode.spcNode = InvalidOid;
|
|
rnode.bucketNode = InvalidBktId;
|
|
break;
|
|
}
|
|
|
|
if (!OidIsValid(rnode.relNode)) {
|
|
ReleaseSysCache(tuple);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
ReleaseSysCache(tuple);
|
|
path = relpathbackend(rnode, backend, MAIN_FORKNUM);
|
|
PG_RETURN_TEXT_P(cstring_to_text(path));
|
|
}
|
|
|
|
/*
|
|
* Get the pathname (relative to $PGDATA) of a relation
|
|
*
|
|
* See comments for pg_relation_filenode.
|
|
*/
|
|
Datum pg_relation_filepath(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relid = PG_GETARG_OID(0);
|
|
HeapTuple tuple;
|
|
Form_pg_class relform;
|
|
RelFileNode rnode;
|
|
BackendId backend;
|
|
char* path = NULL;
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tuple))
|
|
PG_RETURN_NULL();
|
|
relform = (Form_pg_class)GETSTRUCT(tuple);
|
|
|
|
switch (relform->relkind) {
|
|
case RELKIND_RELATION:
|
|
case RELKIND_MATVIEW:
|
|
case RELKIND_INDEX:
|
|
case RELKIND_GLOBAL_INDEX:
|
|
case RELKIND_SEQUENCE:
|
|
case RELKIND_TOASTVALUE:
|
|
/* okay, these have storage */
|
|
|
|
/* This logic should match RelationInitPhysicalAddr */
|
|
rnode.spcNode = ConvertToRelfilenodeTblspcOid(relform->reltablespace);
|
|
if (rnode.spcNode == GLOBALTABLESPACE_OID)
|
|
rnode.dbNode = InvalidOid;
|
|
else
|
|
rnode.dbNode = u_sess->proc_cxt.MyDatabaseId;
|
|
if (relform->relfilenode)
|
|
rnode.relNode = relform->relfilenode;
|
|
else /* Consult the relation mapper */
|
|
rnode.relNode = RelationMapOidToFilenode(relid, relform->relisshared);
|
|
rnode.bucketNode = InvalidBktId;
|
|
break;
|
|
|
|
default:
|
|
/* no storage, return NULL */
|
|
rnode.relNode = InvalidOid;
|
|
/* some compilers generate warnings without these next two lines */
|
|
rnode.dbNode = InvalidOid;
|
|
rnode.spcNode = InvalidOid;
|
|
rnode.bucketNode = InvalidBktId;
|
|
break;
|
|
}
|
|
|
|
if (!OidIsValid(rnode.relNode)) {
|
|
ReleaseSysCache(tuple);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Determine owning backend. */
|
|
switch (relform->relpersistence) {
|
|
case RELPERSISTENCE_UNLOGGED:
|
|
case RELPERSISTENCE_PERMANENT:
|
|
case RELPERSISTENCE_TEMP:
|
|
backend = InvalidBackendId;
|
|
break;
|
|
|
|
case RELPERSISTENCE_GLOBAL_TEMP:
|
|
backend = BackendIdForTempRelations;
|
|
break;
|
|
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
errmsg("invalid relpersistence: %c", relform->relpersistence)));
|
|
backend = InvalidBackendId; /* placate compiler */
|
|
break;
|
|
}
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
path = relpathbackend(rnode, backend, MAIN_FORKNUM);
|
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(path));
|
|
}
|
|
|
|
#ifdef PGXC
|
|
|
|
/*
|
|
* pgxc_tablespace_size
|
|
* Given a tablespace oid, return sum of pg_tablespace_size() executed on all the Datanodes
|
|
*/
|
|
static Datum pgxc_tablespace_size(Oid tsOid)
|
|
{
|
|
StringInfoData buf;
|
|
char* tsname = get_tablespace_name(tsOid);
|
|
ParallelFunctionState* state = NULL;
|
|
int64 size = 0;
|
|
|
|
if (NULL == tsname)
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace with OID %u does not exist", tsOid)));
|
|
|
|
initStringInfo(&buf);
|
|
appendStringInfo(&buf, "SELECT pg_catalog.pg_tablespace_size('%s')", tsname);
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncSum);
|
|
size = state->result;
|
|
FreeParallelFunctionState(state);
|
|
|
|
/*
|
|
* If the type of specific tablespace is HDFS , calculate size of database on HDFS.
|
|
*/
|
|
size += calculate_tablespace_size_on_dfs(tsOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* @Description: get the source database size from CN.
|
|
* @return: the size of database on all DNs.
|
|
*/
|
|
static Datum pgxc_source_database_size()
|
|
{
|
|
StringInfoData buf;
|
|
ParallelFunctionState* state = NULL;
|
|
int64 size = 0;
|
|
/*the default compressed ratio of hdfs tabls is 3 now.*/
|
|
const double hdfs_ratio = 3;
|
|
|
|
initStringInfo(&buf);
|
|
appendStringInfo(&buf, "SELECT pg_catalog.get_db_source_datasize()");
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncSum);
|
|
size = state->result;
|
|
|
|
FreeParallelFunctionState(state);
|
|
|
|
/*
|
|
* If the specified database exists on HDFS, calculate size of database on HDFS.
|
|
*/
|
|
size += calculate_dbsize_on_dfs(u_sess->proc_cxt.MyDatabaseId) * hdfs_ratio;
|
|
|
|
pfree_ext(buf.data);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* pgxc_database_size
|
|
* Given a dboid, return sum of pg_database_size() executed on all the Datanodes
|
|
*/
|
|
static Datum pgxc_database_size(Oid dbOid)
|
|
{
|
|
StringInfoData buf;
|
|
char* dbname = get_and_check_db_name(dbOid, true);
|
|
ParallelFunctionState* state = NULL;
|
|
int64 size = 0;
|
|
|
|
initStringInfo(&buf);
|
|
appendStringInfo(&buf, "SELECT pg_catalog.pg_database_size('%s')", dbname);
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncSum);
|
|
size = state->result;
|
|
|
|
FreeParallelFunctionState(state);
|
|
|
|
/*
|
|
* If the specified database exists on HDFS, calculate size of database on HDFS.
|
|
*/
|
|
size += calculate_dbsize_on_dfs(dbOid);
|
|
|
|
PG_RETURN_INT64(size);
|
|
}
|
|
|
|
/*
|
|
* pgxc_execute_on_nodes
|
|
* Execute 'query' on all the nodes in 'nodelist', and returns int64 datum
|
|
* which has the sum of all the results. If multiples nodes are involved, it
|
|
* assumes that the query returns exactly one row with one attribute of type
|
|
* int64. If there is a single node, it just returns the datum as-is without
|
|
* checking the type of the returned value.
|
|
*/
|
|
Datum pgxc_execute_on_nodes(int numnodes, Oid* nodelist, char* query)
|
|
{
|
|
StringInfoData buf;
|
|
int ret;
|
|
TupleDesc spi_tupdesc;
|
|
int i;
|
|
int64 total_size = 0;
|
|
int64 size = 0;
|
|
bool isnull = false;
|
|
char* nodename = NULL;
|
|
Datum datum = (Datum)0;
|
|
|
|
/*
|
|
* Connect to SPI manager
|
|
*/
|
|
if ((ret = SPI_connect()) < 0)
|
|
/* internal error */
|
|
ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI connect failure - returned %d", ret)));
|
|
|
|
initStringInfo(&buf);
|
|
|
|
/* Get pg_***_size function results from all Datanodes */
|
|
for (i = 0; i < numnodes; i++) {
|
|
nodename = get_pgxc_nodename((Oid)nodelist[i]);
|
|
|
|
ret = SPI_execute_direct(query, nodename);
|
|
spi_tupdesc = SPI_tuptable->tupdesc;
|
|
|
|
if (ret != SPI_OK_SELECT) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SPI_EXECUTE_FAILURE),
|
|
errmsg("failed to execute query '%s' on node '%s'", query, nodename)));
|
|
}
|
|
|
|
if (SPI_processed != 1) {
|
|
// will retry
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CONNECTION_RECEIVE_WRONG), errmsg("expected 1 row, actual %u row", SPI_processed)));
|
|
}
|
|
|
|
/*
|
|
* The query must always return one row having one column:
|
|
*/
|
|
Assert(SPI_processed == 1 && spi_tupdesc->natts == 1);
|
|
|
|
datum = SPI_getbinval(SPI_tuptable->vals[0], spi_tupdesc, 1, &isnull);
|
|
|
|
/* For single node, don't assume the type of datum. It can be bool also. */
|
|
if (numnodes == 1)
|
|
break;
|
|
|
|
size = DatumGetInt64(datum);
|
|
total_size += size;
|
|
}
|
|
|
|
SPI_finish();
|
|
|
|
if (numnodes == 1)
|
|
PG_RETURN_DATUM(datum);
|
|
else
|
|
PG_RETURN_INT64(total_size);
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Target : get table oid from toast table name
|
|
* Brief : toast table name always like pg_toast_table_oid, get toast table name by its oid,
|
|
* : then get base table oid by spliting toast table name using function text_substr_no_len_orclcompat.
|
|
* Description :
|
|
* Input :
|
|
* Output :
|
|
* Notes :
|
|
*/
|
|
Oid pg_toast_get_baseid(Oid relOid, bool* isPartToast)
|
|
{
|
|
Oid base_table_oid;
|
|
const char* table_oid_str = NULL;
|
|
Datum toast_len = Int32GetDatum(10);
|
|
Relation rel;
|
|
|
|
rel = relation_open(relOid, AccessShareLock);
|
|
|
|
if (strstr(RelationGetRelationName(rel), "part") != NULL) {
|
|
*isPartToast = true;
|
|
|
|
toast_len = Int32GetDatum(15);
|
|
}
|
|
|
|
table_oid_str = TextDatumGetCString(DirectFunctionCall2(
|
|
text_substr_no_len_orclcompat, CStringGetTextDatum(RelationGetRelationName(rel)), toast_len));
|
|
base_table_oid = DatumGetInt32(DirectFunctionCall1(int4in, CStringGetDatum(table_oid_str)));
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
return base_table_oid;
|
|
}
|
|
|
|
bool IsToastRelationbyOid(Oid tbl_oid)
|
|
{
|
|
Relation rel;
|
|
bool result = false;
|
|
rel = relation_open(tbl_oid, AccessShareLock);
|
|
result = IsToastNamespace(RelationGetNamespace(rel));
|
|
relation_close(rel, AccessShareLock);
|
|
return result;
|
|
}
|
|
|
|
static bool IsIndexRelationbyOid(Oid tbl_oid)
|
|
{
|
|
Relation rel;
|
|
bool result = false;
|
|
rel = relation_open(tbl_oid, AccessShareLock);
|
|
result = RelationIsIndex(rel);
|
|
relation_close(rel, AccessShareLock);
|
|
return result;
|
|
}
|
|
|
|
static void AddRemoteToastBuf(bool isPartToast, Relation rel, char* funcname, char* extra_arg,
|
|
Relation partTableRel, Partition part, StringInfo buf)
|
|
{
|
|
|
|
// toast table is belog to a ordinary table
|
|
if (!isPartToast) {
|
|
char *relname = RelationGetRelationName(rel);
|
|
relname = repairObjectName(relname);
|
|
if (NULL == extra_arg) {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(a.oid) from pg_class a,pg_class b where a.oid = b.reltoastrelid and "
|
|
"b.relname='%s'",
|
|
funcname,
|
|
relname);
|
|
} else {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(a.oid, '%s') from pg_class a,pg_class b where a.oid = b.reltoastrelid and "
|
|
"b.relname='%s'",
|
|
funcname,
|
|
extra_arg,
|
|
relname);
|
|
}
|
|
}
|
|
// toast table is belog to a partitoin
|
|
else {
|
|
char* partTableName = RelationGetRelationName(partTableRel);
|
|
char* partName = PartitionGetPartitionName(part);
|
|
|
|
partTableName = repairObjectName(partTableName);
|
|
partName = repairObjectName(partName);
|
|
|
|
if (NULL == extra_arg) {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(toast_table.oid) from pg_class toast_table, pg_class part_table, "
|
|
"pg_partition part where toast_table.oid = part.reltoastrelid and part_table.oid=part.parentid and "
|
|
"part_table.relname='%s' and part.relname='%s'",
|
|
funcname,
|
|
partTableName,
|
|
partName);
|
|
} else {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(toast_table.oid, '%s') from pg_class toast_table, pg_class part_table, "
|
|
"pg_partition part where toast_table.oid = part.reltoastrelid and part_table.oid=part.parentid and "
|
|
"part_table.relname='%s' and part.relname='%s'",
|
|
funcname,
|
|
extra_arg,
|
|
partTableName,
|
|
partName);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AddRemoteMatviewBuf(Relation rel, char* funcname, char* extra_arg,
|
|
MvRelationType matviewRelationType, StringInfo buf)
|
|
{
|
|
char* relname = RelationGetRelationName(rel);
|
|
relname = repairObjectName(relname);
|
|
if (NULL == extra_arg) {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(a.oid) from pg_class a, "
|
|
"(select oid from pg_class b where b.relname='%s') c "
|
|
"where a.relname = (select concat('%s', CAST(c.oid as varchar)))",
|
|
funcname,
|
|
relname,
|
|
matviewRelationType == MATVIEW_MAP ? MATMAPNAME : MLOGNAME);
|
|
} else {
|
|
appendStringInfo(buf,
|
|
"SELECT pg_catalog.%s(a.oid, '%s') from pg_class a, "
|
|
"(select oid from pg_class b where b.relname='%s') c "
|
|
"where a.relname = (select concat('%s', CAST(c.oid as varchar)))",
|
|
funcname,
|
|
extra_arg,
|
|
relname,
|
|
matviewRelationType == MATVIEW_MAP ? MATMAPNAME : MLOGNAME);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* pgxc_exec_sizefunc
|
|
* Execute the given object size system function on all the Datanodes associated
|
|
* with relOid, and return the sum of all.
|
|
*
|
|
* Args:
|
|
*
|
|
* relOid: Oid of the table for which the object size function is to be executed.
|
|
*
|
|
* funcname: Name of the system function.
|
|
*
|
|
* extra_arg: The first argument to such sys functions is always table name.
|
|
* Some functions can have a second argument. To pass this argument, extra_arg
|
|
* is used. Currently only pg_relation_size() is the only one that requires
|
|
* a 2nd argument: fork text.
|
|
*/
|
|
static int64 pgxc_exec_sizefunc(Oid relOid, char* funcname, char* extra_arg)
|
|
{
|
|
int numnodes;
|
|
Oid* nodelist = NULL;
|
|
Oid tbl_oid;
|
|
char* relname = NULL;
|
|
StringInfoData buf;
|
|
Relation rel = NULL;
|
|
bool isPartToast = false;
|
|
Oid partTableOid = InvalidOid;
|
|
Relation partTableRel = NULL;
|
|
Partition part = NULL;
|
|
ExecNodes* exec_nodes = makeNode(ExecNodes);
|
|
int i = 0;
|
|
ParallelFunctionState* state = NULL;
|
|
int64 size = 0;
|
|
MvRelationType matviewRelationType = MATVIEW_NOT;
|
|
|
|
if (IsToastRelationbyOid(relOid)) {
|
|
tbl_oid = pg_toast_get_baseid(relOid, &isPartToast);
|
|
} else if (IsMatviewRelationbyOid(relOid, &matviewRelationType)) {
|
|
tbl_oid = MatviewRelationGetBaseid(relOid, matviewRelationType);
|
|
} else {
|
|
tbl_oid = relOid;
|
|
}
|
|
|
|
if (!isPartToast) {
|
|
rel = relation_open(tbl_oid, AccessShareLock);
|
|
} else {
|
|
partTableOid = partid_get_parentid(tbl_oid);
|
|
partTableRel = relation_open(partTableOid, AccessShareLock);
|
|
part = partitionOpen(partTableRel, tbl_oid, AccessShareLock);
|
|
}
|
|
|
|
initStringInfo(&buf);
|
|
|
|
if (IsToastRelationbyOid(relOid)) {
|
|
AddRemoteToastBuf(isPartToast, rel, funcname, extra_arg, partTableRel, part, &buf);
|
|
} else if (IsMatviewRelationbyOid(relOid, &matviewRelationType)) {
|
|
AddRemoteMatviewBuf(rel, funcname, extra_arg, matviewRelationType, &buf);
|
|
} else {
|
|
/* get relation name including any needed schema prefix and quoting */
|
|
relname =
|
|
quote_qualified_identifier(get_namespace_name(rel->rd_rel->relnamespace), RelationGetRelationName(rel));
|
|
relname = repairObjectName(relname);
|
|
NULL == extra_arg ? appendStringInfo(&buf, "SELECT pg_catalog.%s('%s')", funcname, relname) :
|
|
appendStringInfo(&buf, "SELECT pg_catalog.%s('%s', '%s')", funcname, relname, extra_arg);
|
|
}
|
|
|
|
if (!isPartToast) {
|
|
Oid tabRelOid = InvalidOid;
|
|
RelationIsIndex(rel) ? tabRelOid = rel->rd_index->indrelid : tabRelOid = RelationGetRelid(rel);
|
|
|
|
numnodes = get_pgxc_classnodes(tabRelOid, &nodelist);
|
|
relation_close(rel, AccessShareLock);
|
|
} else {
|
|
numnodes = get_pgxc_classnodes(RelationGetRelid(partTableRel), &nodelist);
|
|
partitionClose(partTableRel, part, AccessShareLock);
|
|
relation_close(partTableRel, AccessShareLock);
|
|
}
|
|
|
|
// Build nodes to execute on
|
|
//
|
|
for (i = 0; i < numnodes; i++)
|
|
exec_nodes->nodeList = lappend_int(exec_nodes->nodeList, PGXCNodeGetNodeId(nodelist[i], PGXC_NODE_DATANODE));
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, exec_nodes, StrategyFuncSum);
|
|
size = state->result;
|
|
FreeParallelFunctionState(state);
|
|
|
|
/*
|
|
* If the relation is DFS table, must calculate data size on hdfs.
|
|
*/
|
|
if (RelationIsPaxFormatByOid(relOid)) {
|
|
Relation rel = relation_open(relOid, AccessShareLock);
|
|
StringInfo dfsStorePath = getDfsStorePath(rel);
|
|
Oid tblSpcOid = rel->rd_rel->reltablespace;
|
|
size += calculate_dir_size_on_dfs(tblSpcOid, dfsStorePath);
|
|
relation_close(rel, NoLock);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static int64 pgxc_exec_partition_sizefunc(Oid partTableOid, Oid partOid, char* funcName)
|
|
{
|
|
int numnodes = 0;
|
|
Oid* nodelist = NULL;
|
|
char* partTableName = NULL;
|
|
char* partName = NULL;
|
|
Relation partTableRel = NULL;
|
|
Partition partition = NULL;
|
|
StringInfoData buf;
|
|
ExecNodes* exec_nodes = makeNode(ExecNodes);
|
|
int i = 0;
|
|
ParallelFunctionState* state = NULL;
|
|
int64 size = 0;
|
|
|
|
partTableRel = relation_open(partTableOid, AccessShareLock);
|
|
partition = partitionOpen(partTableRel, partOid, AccessShareLock);
|
|
|
|
initStringInfo(&buf);
|
|
|
|
if (partTableRel->rd_locator_info) {
|
|
// get relation name including any needed schema prefix and quoting
|
|
partTableName = quote_qualified_identifier(
|
|
get_namespace_name(partTableRel->rd_rel->relnamespace), RelationGetRelationName(partTableRel));
|
|
partTableName = repairObjectName(partTableName);
|
|
}
|
|
partName = partition->pd_part->relname.data;
|
|
partName = repairObjectName(partName);
|
|
|
|
appendStringInfo(&buf, "SELECT pg_catalog.%s('%s', '%s')", funcName, partTableName, partName);
|
|
|
|
numnodes = get_pgxc_classnodes(RelationGetRelid(partTableRel), &nodelist);
|
|
|
|
partitionClose(partTableRel, partition, AccessShareLock);
|
|
relation_close(partTableRel, AccessShareLock);
|
|
|
|
// Build nodes to execute on
|
|
//
|
|
for (i = 0; i < numnodes; i++)
|
|
exec_nodes->nodeList = lappend_int(exec_nodes->nodeList, PGXCNodeGetNodeId(nodelist[i], PGXC_NODE_DATANODE));
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, exec_nodes, StrategyFuncSum);
|
|
size = state->result;
|
|
FreeParallelFunctionState(state);
|
|
|
|
return size;
|
|
}
|
|
|
|
// query the relation's compression attrubute
|
|
//
|
|
Datum pg_relation_with_compression(PG_FUNCTION_ARGS)
|
|
{
|
|
text* relname = PG_GETARG_TEXT_P(0);
|
|
List* name = textToQualifiedNameList(relname);
|
|
RangeVar* relrv = makeRangeVarFromNameList(name);
|
|
Relation rel = relation_openrv(relrv, AccessShareLock);
|
|
|
|
if (rel == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
bool compressed = false;
|
|
if (RelationIsColStore(rel))
|
|
compressed = (0 != pg_strcasecmp(COMPRESSION_NO, RelationGetCompression(rel)));
|
|
else
|
|
compressed = RowRelationIsCompressed(rel);
|
|
relation_close(rel, AccessShareLock);
|
|
list_free_ext(name);
|
|
PG_RETURN_BOOL(compressed);
|
|
}
|
|
|
|
// query the relation's compression ratio
|
|
//
|
|
Datum pg_relation_compression_ratio(PG_FUNCTION_ARGS)
|
|
{
|
|
// Need complete it
|
|
//
|
|
PG_RETURN_FLOAT4(1.0);
|
|
}
|
|
|
|
/*
|
|
* @Description: the table size, include index
|
|
* @IN rel: relation of a table
|
|
* @Return: table size
|
|
* @See also:
|
|
*/
|
|
uint64 pg_relation_perm_table_size(Relation rel)
|
|
{
|
|
/* get table size, include index */
|
|
return (uint64)calculate_total_relation_size(rel);
|
|
}
|
|
|
|
/*
|
|
* @Description: the table size
|
|
* @IN rel: relation of a table
|
|
* @Return: table size
|
|
* @See also:
|
|
*/
|
|
uint64 pg_relation_table_size(Relation rel)
|
|
{
|
|
/* get table size */
|
|
return (uint64)calculate_table_size(rel, DEFAULT_FORKNUM);
|
|
}
|
|
|
|
/*
|
|
* @Description : Parallel get max database size in all datanodes.
|
|
* @in : group_name, nodegroup name
|
|
* @out : None
|
|
* @return : max database size in all datanodes of the nodegroup.
|
|
*/
|
|
Datum pgxc_max_datanode_size_name(PG_FUNCTION_ARGS)
|
|
{
|
|
const char* group_name = NULL;
|
|
int64 size;
|
|
Oid group_oid;
|
|
ExecNodes* exec_nodes = NULL;
|
|
Oid* members = NULL;
|
|
int nmembers;
|
|
int64 max_size = 0;
|
|
TupleTableSlot* slot = NULL;
|
|
Name str = PG_GETARG_NAME(0);
|
|
|
|
group_name = str->data;
|
|
|
|
/* Only a DB administrator can remove cluster node groups */
|
|
if (!superuser())
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be system admin to call gs_get_max_dbsize_name.")));
|
|
|
|
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
|
|
StringInfoData buf;
|
|
ParallelFunctionState* state = NULL;
|
|
|
|
group_oid = get_pgxc_groupoid(group_name);
|
|
|
|
if (!OidIsValid(group_oid))
|
|
ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("PGXC Group %s: group not defined", group_name)));
|
|
|
|
nmembers = get_pgxc_groupmembers(group_oid, &members);
|
|
if (nmembers == 0)
|
|
PG_RETURN_INT64(0);
|
|
|
|
exec_nodes = makeNode(ExecNodes);
|
|
|
|
for (int i = 0; i < nmembers; i++) {
|
|
int nodeId = PGXCNodeGetNodeId(members[i], PGXC_NODE_DATANODE);
|
|
exec_nodes->nodeList = lappend_int(exec_nodes->nodeList, nodeId);
|
|
}
|
|
|
|
initStringInfo(&buf);
|
|
appendStringInfo(&buf, "SELECT SUM(pg_database_size(oid))::bigint FROM pg_database;");
|
|
|
|
state = RemoteFunctionResultHandler(buf.data, exec_nodes, NULL, false);
|
|
|
|
Assert(state && state->tupstore && state->tupdesc);
|
|
slot = MakeSingleTupleTableSlot(state->tupdesc);
|
|
|
|
for (;;) {
|
|
bool isnull = false;
|
|
|
|
if (!tuplestore_gettupleslot(state->tupstore, true, false, slot))
|
|
break;
|
|
|
|
size = DatumGetInt64(tableam_tslot_getattr(slot, 1, &isnull));
|
|
if (!isnull && max_size < size)
|
|
max_size = size;
|
|
|
|
ExecClearTuple(slot);
|
|
}
|
|
|
|
ExecDropSingleTupleTableSlot(slot);
|
|
|
|
FreeParallelFunctionState(state);
|
|
}
|
|
|
|
PG_RETURN_INT64(max_size);
|
|
}
|
|
|
|
/*
|
|
* Indicate whether a relation is scannable.
|
|
*
|
|
* Currently, this is always true except for a materialized view which has not
|
|
* been populated.
|
|
*/
|
|
Datum
|
|
pg_relation_is_scannable(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relid;
|
|
Relation relation;
|
|
bool result;
|
|
|
|
relid = PG_GETARG_OID(0);
|
|
relation = RelationIdGetRelation(relid);
|
|
result = relation->rd_isscannable;
|
|
RelationClose(relation);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
#endif /* PGXC */
|