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

Lines changed: 37 additions & 41 deletions
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

Lines changed: 77 additions & 96 deletions
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)
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