diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index f53d411ad1dd7..a5464e0a27872 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -45,7 +45,6 @@ #include "utils/builtins.h" #include "utils/timestamp.h" - /* * Common subroutine for num_nulls() and num_nonnulls(). * Returns true if successful, false if function should return NULL. @@ -361,12 +360,201 @@ pg_rotate_logfile(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } -/* Function to find out which databases make use of a tablespace */ +/* Structures used in pg_objs_per_tablespace */ +typedef struct +{ + char *databaseOid; + char *relationOid; +} tbsp_record; + +typedef struct +{ + tbsp_record *records; + int reccount; +} tbsp_fctx; + +/* + * Check if a char* array is a number. I don't know if there is + * something that does the same thing in the codebase, so I wrote + * my own function. + */ +static int +check_is_digit(const char *numArray, int *value) +{ + int n = 0; + return sscanf(numArray, "%d%n", value, &n) > 0 /* integer was converted */ + && numArray[n] == '\0'; /* all input got consumed */ +} + +/* + * Function to report which objects are in which tablespace. + * It follows a similar approach as pg_tablespace_databases, ie, + * reads the tablespace's directory and list all OIDs in it. + * + */ +Datum +pg_objs_per_tablespace(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + char *values[2]; + DIR *subdirdesc; + DIR *dirdesc; + char *location; + HeapTuple tuple; + struct dirent *direntry; + struct dirent *subdirentry; + tbsp_record *records = NULL; + int maxrecords = 0; + tbsp_fctx *fctx; + int t; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + Oid tablespaceOid = PG_GETARG_OID(0); + + /* If user is not superuser, return a error message and block execution */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to execute this function")))); + + /* + * Global tablespace does not hold databases. If OID passed is from global tablespace + * return a error and block execution. + */ + if (tablespaceOid == GLOBALTABLESPACE_OID) + { + ereport(ERROR, + (errcode(ERRCODE_NO_DATA), + (errmsg("global tablespace never has databases")))); + } + else + { + /* if OID passed is from default tablespace point location to base directory. + * Otherwise point location to tablespace's directory. + */ + if (tablespaceOid == DEFAULTTABLESPACE_OID) + location = psprintf("base"); + else + location = psprintf("pg_tblspc/%u/%s", tablespaceOid, + TABLESPACE_VERSION_DIRECTORY); + } + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + tupdesc = CreateTemplateTupleDesc(2, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "database", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relation", + TEXTOID, -1, 0); + + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + /* allocate directory descriptor. If directory descriptor is null, tablespace does not exists. + * In this case, return a error and block execution. + */ + dirdesc = AllocateDir(location); + if (!dirdesc) + ereport(ERROR, + (errcode(ERRCODE_NO_DATA), + (errmsg("%u is not a valid tablespace Oid", tablespaceOid)))); + + /* Reads the tablespace's directory. If it's a tablespace it will contain subdirectories for each + * database, each one with their own objects. + */ + while ((direntry = ReadDir(dirdesc, location)) != NULL) + { + char *subdir; + + /* ignore parent directories */ + if (direntry->d_name[0] == '.') + continue; + + /* if directory entry is a directory then it is a database OID. Iterate over it go get the + * objects in the database. + */ + if (direntry->d_type == DT_DIR) + { + /* point subdir location to tablespace/database directory */ + subdir = psprintf("%s/%s", location, direntry->d_name); + + /* allocate a descriptor to the tablespace/database location + * TODO : Test if directory is invalid or does not exists + */ + subdirdesc = AllocateDir(subdir); + + /* iterate over tablespace/database directory and get the objects */ + while ((subdirentry = ReadDir(subdirdesc, subdir)) != NULL) + { + + /* ignore parent directories */ + if (subdirentry->d_name[0] == '.') + continue; + + /* check if entry is a number. Do not consider _vm, _fsm and other files */ + if (!check_is_digit(subdirentry->d_name, &t)) + continue; + + /* If it's the first time, allocate one record into the records array and + * add a database/object value pair. + */ + if (maxrecords == 0) + { + records = (tbsp_record*) malloc(sizeof(tbsp_record)); + records[0].databaseOid = get_database_name(atooid(direntry->d_name)); + records[0].relationOid = strdup(subdirentry->d_name); + maxrecords++; + } + else + { + /* If the records array is already initialized, realloc one more record and + * add a database/object value pair into it. + */ + records = (tbsp_record*) realloc(records, (maxrecords+1) * sizeof(tbsp_record)); + records[maxrecords].databaseOid = get_database_name(atooid(direntry->d_name)); + records[maxrecords].relationOid = strdup(subdirentry->d_name); + maxrecords++; + } + } + FreeDir(subdirdesc); + } + } + FreeDir(dirdesc); + + /* Initialize the context struct used over SRF_PERCALL iterations */ + fctx = palloc(sizeof(tbsp_fctx)); + + /* assign values to the context struct used to maintain the status over SRF_PERCALL iterations */ + fctx->records = records; + fctx->reccount = maxrecords; + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + fctx = (tbsp_fctx *) funcctx->user_fctx; + + /* iterate over the array of records and return a tuple to each database/object value pair */ + if (funcctx->call_cntr < fctx->reccount) + { + values[0] = fctx->records[funcctx->call_cntr].databaseOid; + values[1] = fctx->records[funcctx->call_cntr].relationOid; + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + SRF_RETURN_DONE(funcctx); +} +/* Function to find out which databases make use of a tablespace */ typedef struct { - char *location; - DIR *dirdesc; + char *location; + DIR *dirdesc; } ts_db_fctx; Datum diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 830bab37eae04..3123413c64778 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4279,6 +4279,9 @@ DATA(insert OID = 2550 ( integer_pl_date PGNSP PGUID 14 1 0 0 0 f f f f t f i DATA(insert OID = 2556 ( pg_tablespace_databases PGNSP PGUID 12 1 1000 0 0 f f f f t t s s 1 0 26 "26" _null_ _null_ _null_ _null_ _null_ pg_tablespace_databases _null_ _null_ _null_ )); DESCR("get OIDs of databases in a tablespace"); +DATA(insert OID = 2579 ( pg_objs_per_tablespace PGNSP PGUID 12 1 0 0 0 f f f f t t s s 1 0 2249 "26" _null_ _null_ _null_ _null_ _null_ pg_objs_per_tablespace _null_ _null_ _null_ )); +DESCR("get OIDs objects per database and tablespace"); + DATA(insert OID = 2557 ( bool PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 16 "23" _null_ _null_ _null_ _null_ _null_ int4_bool _null_ _null_ _null_ )); DESCR("convert int4 to boolean"); DATA(insert OID = 2558 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "16" _null_ _null_ _null_ _null_ _null_ bool_int4 _null_ _null_ _null_ )); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy