Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b4570d3

Browse files
committed
Avoid holding a directory FD open across assorted SRF calls.
This extends the fixes made in commit 085b6b6 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
1 parent 1137581 commit b4570d3

File tree

10 files changed

+388
-348
lines changed

10 files changed

+388
-348
lines changed

contrib/adminpack/adminpack.c

+37-41
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ static int64 pg_file_write_internal(text *file, text *data, bool replace);
5656
static bool pg_file_rename_internal(text *file1, text *file2, text *file3);
5757
static Datum pg_logdir_ls_internal(FunctionCallInfo fcinfo);
5858

59-
typedef struct
60-
{
61-
char *location;
62-
DIR *dirdesc;
63-
} directory_fctx;
6459

6560
/*-----------------------
6661
* some helper functions
@@ -504,50 +499,51 @@ pg_logdir_ls_v1_1(PG_FUNCTION_ARGS)
504499
static Datum
505500
pg_logdir_ls_internal(FunctionCallInfo fcinfo)
506501
{
507-
FuncCallContext *funcctx;
502+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
503+
bool randomAccess;
504+
TupleDesc tupdesc;
505+
Tuplestorestate *tupstore;
506+
AttInMetadata *attinmeta;
507+
DIR *dirdesc;
508508
struct dirent *de;
509-
directory_fctx *fctx;
509+
MemoryContext oldcontext;
510510

511511
if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
512512
ereport(ERROR,
513513
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
514514
errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")));
515515

516-
if (SRF_IS_FIRSTCALL())
517-
{
518-
MemoryContext oldcontext;
519-
TupleDesc tupdesc;
520-
521-
funcctx = SRF_FIRSTCALL_INIT();
522-
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
523-
524-
fctx = palloc(sizeof(directory_fctx));
525-
526-
tupdesc = CreateTemplateTupleDesc(2);
527-
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
528-
TIMESTAMPOID, -1, 0);
529-
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
530-
TEXTOID, -1, 0);
516+
/* check to see if caller supports us returning a tuplestore */
517+
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
518+
ereport(ERROR,
519+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
520+
errmsg("set-valued function called in context that cannot accept a set")));
521+
if (!(rsinfo->allowedModes & SFRM_Materialize))
522+
ereport(ERROR,
523+
(errcode(ERRCODE_SYNTAX_ERROR),
524+
errmsg("materialize mode required, but it is not allowed in this context")));
531525

532-
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
526+
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
527+
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
533528

534-
fctx->location = pstrdup(Log_directory);
535-
fctx->dirdesc = AllocateDir(fctx->location);
529+
tupdesc = CreateTemplateTupleDesc(2);
530+
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
531+
TIMESTAMPOID, -1, 0);
532+
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
533+
TEXTOID, -1, 0);
536534

537-
if (!fctx->dirdesc)
538-
ereport(ERROR,
539-
(errcode_for_file_access(),
540-
errmsg("could not open directory \"%s\": %m",
541-
fctx->location)));
535+
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
536+
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
537+
rsinfo->returnMode = SFRM_Materialize;
538+
rsinfo->setResult = tupstore;
539+
rsinfo->setDesc = tupdesc;
542540

543-
funcctx->user_fctx = fctx;
544-
MemoryContextSwitchTo(oldcontext);
545-
}
541+
MemoryContextSwitchTo(oldcontext);
546542

547-
funcctx = SRF_PERCALL_SETUP();
548-
fctx = (directory_fctx *) funcctx->user_fctx;
543+
attinmeta = TupleDescGetAttInMetadata(tupdesc);
549544

550-
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
545+
dirdesc = AllocateDir(Log_directory);
546+
while ((de = ReadDir(dirdesc, Log_directory)) != NULL)
551547
{
552548
char *values[2];
553549
HeapTuple tuple;
@@ -584,13 +580,13 @@ pg_logdir_ls_internal(FunctionCallInfo fcinfo)
584580
/* Seems the timestamp is OK; prepare and return tuple */
585581

586582
values[0] = timestampbuf;
587-
values[1] = psprintf("%s/%s", fctx->location, de->d_name);
583+
values[1] = psprintf("%s/%s", Log_directory, de->d_name);
588584

589-
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
585+
tuple = BuildTupleFromCStrings(attinmeta, values);
590586

591-
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
587+
tuplestore_puttuple(tupstore, tuple);
592588
}
593589

594-
FreeDir(fctx->dirdesc);
595-
SRF_RETURN_DONE(funcctx);
590+
FreeDir(dirdesc);
591+
return (Datum) 0;
596592
}

contrib/pgrowlocks/pgrowlocks.c

+77-96
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,6 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
5454

5555
#define NCHARS 32
5656

57-
typedef struct
58-
{
59-
Relation rel;
60-
TableScanDesc scan;
61-
int ncolumns;
62-
} MyData;
63-
6457
#define Atnum_tid 0
6558
#define Atnum_xmax 1
6659
#define Atnum_ismulti 2
@@ -71,84 +64,86 @@ typedef struct
7164
Datum
7265
pgrowlocks(PG_FUNCTION_ARGS)
7366
{
74-
FuncCallContext *funcctx;
75-
TableScanDesc scan;
76-
HeapScanDesc hscan;
77-
HeapTuple tuple;
67+
text *relname = PG_GETARG_TEXT_PP(0);
68+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
69+
bool randomAccess;
7870
TupleDesc tupdesc;
71+
Tuplestorestate *tupstore;
7972
AttInMetadata *attinmeta;
80-
Datum result;
81-
MyData *mydata;
8273
Relation rel;
74+
RangeVar *relrv;
75+
TableScanDesc scan;
76+
HeapScanDesc hscan;
77+
HeapTuple tuple;
78+
MemoryContext oldcontext;
79+
AclResult aclresult;
80+
char **values;
81+
82+
/* check to see if caller supports us returning a tuplestore */
83+
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
84+
ereport(ERROR,
85+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
86+
errmsg("set-valued function called in context that cannot accept a set")));
87+
if (!(rsinfo->allowedModes & SFRM_Materialize))
88+
ereport(ERROR,
89+
(errcode(ERRCODE_SYNTAX_ERROR),
90+
errmsg("materialize mode required, but it is not allowed in this context")));
91+
92+
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
93+
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
94+
95+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
96+
elog(ERROR, "return type must be a row type");
97+
98+
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
99+
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
100+
rsinfo->returnMode = SFRM_Materialize;
101+
rsinfo->setResult = tupstore;
102+
rsinfo->setDesc = tupdesc;
103+
104+
MemoryContextSwitchTo(oldcontext);
105+
106+
/* Access the table */
107+
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
108+
rel = relation_openrv(relrv, AccessShareLock);
109+
110+
if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
111+
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
112+
errmsg("only heap AM is supported")));
113+
114+
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
115+
ereport(ERROR,
116+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
117+
errmsg("\"%s\" is a partitioned table",
118+
RelationGetRelationName(rel)),
119+
errdetail("Partitioned tables do not contain rows.")));
120+
else if (rel->rd_rel->relkind != RELKIND_RELATION)
121+
ereport(ERROR,
122+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
123+
errmsg("\"%s\" is not a table",
124+
RelationGetRelationName(rel))));
125+
126+
/*
127+
* check permissions: must have SELECT on table or be in
128+
* pg_stat_scan_tables
129+
*/
130+
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
131+
ACL_SELECT);
132+
if (aclresult != ACLCHECK_OK)
133+
aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
134+
135+
if (aclresult != ACLCHECK_OK)
136+
aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
137+
RelationGetRelationName(rel));
138+
139+
/* Scan the relation */
140+
scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
141+
hscan = (HeapScanDesc) scan;
83142

84-
if (SRF_IS_FIRSTCALL())
85-
{
86-
text *relname;
87-
RangeVar *relrv;
88-
MemoryContext oldcontext;
89-
AclResult aclresult;
90-
91-
funcctx = SRF_FIRSTCALL_INIT();
92-
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
93-
94-
/* Build a tuple descriptor for our result type */
95-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
96-
elog(ERROR, "return type must be a row type");
97-
98-
attinmeta = TupleDescGetAttInMetadata(tupdesc);
99-
funcctx->attinmeta = attinmeta;
100-
101-
relname = PG_GETARG_TEXT_PP(0);
102-
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
103-
rel = relation_openrv(relrv, AccessShareLock);
104-
105-
if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
106-
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
107-
errmsg("only heap AM is supported")));
108-
109-
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
110-
ereport(ERROR,
111-
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
112-
errmsg("\"%s\" is a partitioned table",
113-
RelationGetRelationName(rel)),
114-
errdetail("Partitioned tables do not contain rows.")));
115-
else if (rel->rd_rel->relkind != RELKIND_RELATION)
116-
ereport(ERROR,
117-
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
118-
errmsg("\"%s\" is not a table",
119-
RelationGetRelationName(rel))));
120-
121-
/*
122-
* check permissions: must have SELECT on table or be in
123-
* pg_stat_scan_tables
124-
*/
125-
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
126-
ACL_SELECT);
127-
if (aclresult != ACLCHECK_OK)
128-
aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
129-
130-
if (aclresult != ACLCHECK_OK)
131-
aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
132-
RelationGetRelationName(rel));
133-
134-
scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
135-
hscan = (HeapScanDesc) scan;
136-
mydata = palloc(sizeof(*mydata));
137-
mydata->rel = rel;
138-
mydata->scan = scan;
139-
mydata->ncolumns = tupdesc->natts;
140-
funcctx->user_fctx = mydata;
141-
142-
MemoryContextSwitchTo(oldcontext);
143-
}
143+
attinmeta = TupleDescGetAttInMetadata(tupdesc);
144144

145-
funcctx = SRF_PERCALL_SETUP();
146-
attinmeta = funcctx->attinmeta;
147-
mydata = (MyData *) funcctx->user_fctx;
148-
scan = mydata->scan;
149-
hscan = (HeapScanDesc) scan;
145+
values = (char **) palloc(tupdesc->natts * sizeof(char *));
150146

151-
/* scan the relation */
152147
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
153148
{
154149
TM_Result htsu;
@@ -169,10 +164,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
169164
*/
170165
if (htsu == TM_BeingModified)
171166
{
172-
char **values;
173-
174-
values = (char **) palloc(mydata->ncolumns * sizeof(char *));
175-
176167
values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
177168
PointerGetDatum(&tuple->t_self));
178169

@@ -297,16 +288,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
297288

298289
/* build a tuple */
299290
tuple = BuildTupleFromCStrings(attinmeta, values);
300-
301-
/* make the tuple into a datum */
302-
result = HeapTupleGetDatum(tuple);
303-
304-
/*
305-
* no need to pfree what we allocated; it's on a short-lived
306-
* memory context anyway
307-
*/
308-
309-
SRF_RETURN_NEXT(funcctx, result);
291+
tuplestore_puttuple(tupstore, tuple);
310292
}
311293
else
312294
{
@@ -315,7 +297,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
315297
}
316298

317299
table_endscan(scan);
318-
table_close(mydata->rel, AccessShareLock);
319-
320-
SRF_RETURN_DONE(funcctx);
300+
table_close(rel, AccessShareLock);
301+
return (Datum) 0;
321302
}

0 commit comments

Comments
 (0)