mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
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
This commit is contained in:
@@ -36,13 +36,6 @@
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/timestamp.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *location;
|
||||
DIR *dirdesc;
|
||||
bool include_dot_dirs;
|
||||
} directory_fctx;
|
||||
|
||||
|
||||
/*
|
||||
* Convert a "text" filename argument to C string, and check it's allowable.
|
||||
@@ -447,67 +440,79 @@ pg_stat_file_1arg(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
pg_ls_dir(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
char *location;
|
||||
bool missing_ok = false;
|
||||
bool include_dot_dirs = false;
|
||||
bool randomAccess;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
DIR *dirdesc;
|
||||
struct dirent *de;
|
||||
directory_fctx *fctx;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
|
||||
|
||||
/* check the optional arguments */
|
||||
if (PG_NARGS() == 3)
|
||||
{
|
||||
bool missing_ok = false;
|
||||
bool include_dot_dirs = false;
|
||||
|
||||
/* check the optional arguments */
|
||||
if (PG_NARGS() == 3)
|
||||
{
|
||||
if (!PG_ARGISNULL(1))
|
||||
missing_ok = PG_GETARG_BOOL(1);
|
||||
if (!PG_ARGISNULL(2))
|
||||
include_dot_dirs = PG_GETARG_BOOL(2);
|
||||
}
|
||||
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
fctx = palloc(sizeof(directory_fctx));
|
||||
fctx->location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
|
||||
|
||||
fctx->include_dot_dirs = include_dot_dirs;
|
||||
fctx->dirdesc = AllocateDir(fctx->location);
|
||||
|
||||
if (!fctx->dirdesc)
|
||||
{
|
||||
if (missing_ok && errno == ENOENT)
|
||||
{
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not open directory \"%s\": %m",
|
||||
fctx->location)));
|
||||
}
|
||||
funcctx->user_fctx = fctx;
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
if (!PG_ARGISNULL(1))
|
||||
missing_ok = PG_GETARG_BOOL(1);
|
||||
if (!PG_ARGISNULL(2))
|
||||
include_dot_dirs = PG_GETARG_BOOL(2);
|
||||
}
|
||||
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
fctx = (directory_fctx *) funcctx->user_fctx;
|
||||
/* 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")));
|
||||
|
||||
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
|
||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
||||
|
||||
tupdesc = CreateTemplateTupleDesc(1);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_ls_dir", TEXTOID, -1, 0);
|
||||
|
||||
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
|
||||
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
dirdesc = AllocateDir(location);
|
||||
if (!dirdesc)
|
||||
{
|
||||
if (!fctx->include_dot_dirs &&
|
||||
/* Return empty tuplestore if appropriate */
|
||||
if (missing_ok && errno == ENOENT)
|
||||
return (Datum) 0;
|
||||
/* Otherwise, we can let ReadDir() throw the error */
|
||||
}
|
||||
|
||||
while ((de = ReadDir(dirdesc, location)) != NULL)
|
||||
{
|
||||
Datum values[1];
|
||||
bool nulls[1];
|
||||
|
||||
if (!include_dot_dirs &&
|
||||
(strcmp(de->d_name, ".") == 0 ||
|
||||
strcmp(de->d_name, "..") == 0))
|
||||
continue;
|
||||
|
||||
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
|
||||
values[0] = CStringGetTextDatum(de->d_name);
|
||||
nulls[0] = false;
|
||||
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
}
|
||||
|
||||
FreeDir(fctx->dirdesc);
|
||||
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
FreeDir(dirdesc);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -548,8 +553,7 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
|
||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("materialize mode required, but it is not "
|
||||
"allowed in this context")));
|
||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
||||
|
||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
||||
@@ -575,10 +579,7 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
|
||||
{
|
||||
/* Return empty tuplestore if appropriate */
|
||||
if (missing_ok && errno == ENOENT)
|
||||
{
|
||||
tuplestore_donestoring(tupstore);
|
||||
return (Datum) 0;
|
||||
}
|
||||
/* Otherwise, we can let ReadDir() throw the error */
|
||||
}
|
||||
|
||||
@@ -613,7 +614,6 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
|
||||
}
|
||||
|
||||
FreeDir(dirdesc);
|
||||
tuplestore_donestoring(tupstore);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user