aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/misc.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2020-03-16 21:05:28 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2020-03-16 21:05:52 -0400
commitb4570d33aa045df330bb325ba8a2cbf02266a555 (patch)
tree4e7ebfee102862d095bfa9eb0dede58a4cca471f /src/backend/utils/adt/misc.c
parent113758155c11cf993ca0ecee8856e300a2525a30 (diff)
downloadpostgresql-b4570d33aa045df330bb325ba8a2cbf02266a555.tar.gz
postgresql-b4570d33aa045df330bb325ba8a2cbf02266a555.zip
Avoid holding a directory FD open across assorted SRF calls.
This extends the fixes made in commit 085b6b667 to other SRFs with the same bug, namely pg_logdir_ls(), pgrowlocks(), pg_timezone_names(), pg_ls_dir(), and pg_tablespace_databases(). Also adjust various comments and documentation to warn against expecting to clean up resources during a ValuePerCall SRF's final call. Back-patch to all supported branches, since these functions were all born broken. Justin Pryzby, with cosmetic tweaks by me Discussion: https://postgr.es/m/20200308173103.GC1357@telsasoft.com
Diffstat (limited to 'src/backend/utils/adt/misc.c')
-rw-r--r--src/backend/utils/adt/misc.c117
1 files changed, 65 insertions, 52 deletions
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 323e36b81c7..ee340fb0f02 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -194,72 +194,82 @@ current_query(PG_FUNCTION_ARGS)
/* Function to find out which databases make use of a tablespace */
-typedef struct
-{
- char *location;
- DIR *dirdesc;
-} ts_db_fctx;
-
Datum
pg_tablespace_databases(PG_FUNCTION_ARGS)
{
- FuncCallContext *funcctx;
+ Oid tablespaceOid = PG_GETARG_OID(0);
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ bool randomAccess;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ char *location;
+ DIR *dirdesc;
struct dirent *de;
- ts_db_fctx *fctx;
+ MemoryContext oldcontext;
- if (SRF_IS_FIRSTCALL())
- {
- MemoryContext oldcontext;
- Oid tablespaceOid = PG_GETARG_OID(0);
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("materialize mode required, but it is not allowed in this context")));
- funcctx = SRF_FIRSTCALL_INIT();
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
+ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- fctx = palloc(sizeof(ts_db_fctx));
+ tupdesc = CreateTemplateTupleDesc(1);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_tablespace_databases",
+ OIDOID, -1, 0);
- if (tablespaceOid == GLOBALTABLESPACE_OID)
- {
- fctx->dirdesc = NULL;
- ereport(WARNING,
- (errmsg("global tablespace never has databases")));
- }
- else
- {
- if (tablespaceOid == DEFAULTTABLESPACE_OID)
- fctx->location = psprintf("base");
- else
- fctx->location = psprintf("pg_tblspc/%u/%s", tablespaceOid,
- TABLESPACE_VERSION_DIRECTORY);
+ randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
+ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
- fctx->dirdesc = AllocateDir(fctx->location);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
- if (!fctx->dirdesc)
- {
- /* the only expected error is ENOENT */
- if (errno != ENOENT)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not open directory \"%s\": %m",
- fctx->location)));
- ereport(WARNING,
- (errmsg("%u is not a tablespace OID", tablespaceOid)));
- }
- }
- funcctx->user_fctx = fctx;
- MemoryContextSwitchTo(oldcontext);
+ MemoryContextSwitchTo(oldcontext);
+
+ if (tablespaceOid == GLOBALTABLESPACE_OID)
+ {
+ ereport(WARNING,
+ (errmsg("global tablespace never has databases")));
+ /* return empty tuplestore */
+ return (Datum) 0;
}
- funcctx = SRF_PERCALL_SETUP();
- fctx = (ts_db_fctx *) funcctx->user_fctx;
+ if (tablespaceOid == DEFAULTTABLESPACE_OID)
+ location = psprintf("base");
+ else
+ location = psprintf("pg_tblspc/%u/%s", tablespaceOid,
+ TABLESPACE_VERSION_DIRECTORY);
- if (!fctx->dirdesc) /* not a tablespace */
- SRF_RETURN_DONE(funcctx);
+ dirdesc = AllocateDir(location);
- while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
+ if (!dirdesc)
+ {
+ /* the only expected error is ENOENT */
+ if (errno != ENOENT)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ location)));
+ ereport(WARNING,
+ (errmsg("%u is not a tablespace OID", tablespaceOid)));
+ /* return empty tuplestore */
+ return (Datum) 0;
+ }
+
+ while ((de = ReadDir(dirdesc, location)) != NULL)
{
Oid datOid = atooid(de->d_name);
char *subdir;
bool isempty;
+ Datum values[1];
+ bool nulls[1];
/* this test skips . and .., but is awfully weak */
if (!datOid)
@@ -267,18 +277,21 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
/* if database subdir is empty, don't report tablespace as used */
- subdir = psprintf("%s/%s", fctx->location, de->d_name);
+ subdir = psprintf("%s/%s", location, de->d_name);
isempty = directory_is_empty(subdir);
pfree(subdir);
if (isempty)
continue; /* indeed, nothing in it */
- SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(datOid));
+ values[0] = ObjectIdGetDatum(datOid);
+ nulls[0] = false;
+
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
- FreeDir(fctx->dirdesc);
- SRF_RETURN_DONE(funcctx);
+ FreeDir(dirdesc);
+ return (Datum) 0;
}