Skip to content

Commit 94d626f

Browse files
committed
Use materialize SRF mode in brin_page_items
This function was using the single-value-per-call mechanism, but the code relied on a relcache entry that wasn't kept open across calls. This manifested as weird errors in buildfarm during the short time that the "brin-1" isolation test lived. Backpatch to 9.5, where it was introduced.
1 parent 36e863b commit 94d626f

File tree

1 file changed

+101
-122
lines changed

1 file changed

+101
-122
lines changed

contrib/pageinspect/brinfuncs.c

Lines changed: 101 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,6 @@ typedef struct brin_column_state
3737
FmgrInfo outputFn[FLEXIBLE_ARRAY_MEMBER];
3838
} brin_column_state;
3939

40-
typedef struct brin_page_state
41-
{
42-
BrinDesc *bdesc;
43-
Page page;
44-
OffsetNumber offset;
45-
bool unusedItem;
46-
bool done;
47-
AttrNumber attno;
48-
BrinMemTuple *dtup;
49-
brin_column_state *columns[FLEXIBLE_ARRAY_MEMBER];
50-
} brin_page_state;
51-
5240

5341
static Page verify_brin_page(bytea *raw_page, uint16 type,
5442
const char *strtype);
@@ -119,89 +107,89 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
119107
Datum
120108
brin_page_items(PG_FUNCTION_ARGS)
121109
{
122-
brin_page_state *state;
123-
FuncCallContext *fctx;
110+
bytea *raw_page = PG_GETARG_BYTEA_P(0);
111+
Oid indexRelid = PG_GETARG_OID(1);
112+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
113+
TupleDesc tupdesc;
114+
MemoryContext oldcontext;
115+
Tuplestorestate *tupstore;
116+
Relation indexRel;
117+
brin_column_state **columns;
118+
BrinDesc *bdesc;
119+
BrinMemTuple *dtup;
120+
Page page;
121+
OffsetNumber offset;
122+
AttrNumber attno;
123+
bool unusedItem;
124124

125125
if (!superuser())
126126
ereport(ERROR,
127127
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
128128
(errmsg("must be superuser to use raw page functions"))));
129129

130-
if (SRF_IS_FIRSTCALL())
131-
{
132-
bytea *raw_page = PG_GETARG_BYTEA_P(0);
133-
Oid indexRelid = PG_GETARG_OID(1);
134-
Page page;
135-
TupleDesc tupdesc;
136-
MemoryContext mctx;
137-
Relation indexRel;
138-
AttrNumber attno;
139-
140-
/* minimally verify the page we got */
141-
page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
130+
/* check to see if caller supports us returning a tuplestore */
131+
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
132+
ereport(ERROR,
133+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
134+
errmsg("set-valued function called in context that cannot accept a set")));
135+
if (!(rsinfo->allowedModes & SFRM_Materialize) ||
136+
rsinfo->expectedDesc == NULL)
137+
ereport(ERROR,
138+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
139+
errmsg("materialize mode required, but it is not allowed in this context")));
142140

143-
/* create a function context for cross-call persistence */
144-
fctx = SRF_FIRSTCALL_INIT();
141+
/* Build a tuple descriptor for our result type */
142+
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
143+
elog(ERROR, "return type must be a row type");
145144

146-
/* switch to memory context appropriate for multiple function calls */
147-
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
145+
/* Build tuplestore to hold the result rows */
146+
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
148147

149-
/* Build a tuple descriptor for our result type */
150-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
151-
elog(ERROR, "return type must be a row type");
148+
tupstore = tuplestore_begin_heap(true, false, work_mem);
149+
rsinfo->returnMode = SFRM_Materialize;
150+
rsinfo->setResult = tupstore;
151+
rsinfo->setDesc = tupdesc;
152152

153-
indexRel = index_open(indexRelid, AccessShareLock);
153+
MemoryContextSwitchTo(oldcontext);
154154

155-
state = palloc(offsetof(brin_page_state, columns) +
156-
sizeof(brin_column_state) * RelationGetDescr(indexRel)->natts);
155+
indexRel = index_open(indexRelid, AccessShareLock);
156+
bdesc = brin_build_desc(indexRel);
157157

158-
state->bdesc = brin_build_desc(indexRel);
159-
state->page = page;
160-
state->offset = FirstOffsetNumber;
161-
state->unusedItem = false;
162-
state->done = false;
163-
state->dtup = NULL;
158+
/* minimally verify the page we got */
159+
page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
164160

165-
/*
166-
* Initialize output functions for all indexed datatypes; simplifies
167-
* calling them later.
168-
*/
169-
for (attno = 1; attno <= state->bdesc->bd_tupdesc->natts; attno++)
161+
/*
162+
* Initialize output functions for all indexed datatypes; simplifies
163+
* calling them later.
164+
*/
165+
columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts);
166+
for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
167+
{
168+
Oid output;
169+
bool isVarlena;
170+
BrinOpcInfo *opcinfo;
171+
int i;
172+
brin_column_state *column;
173+
174+
opcinfo = bdesc->bd_info[attno - 1];
175+
column = palloc(offsetof(brin_column_state, outputFn) +
176+
sizeof(FmgrInfo) * opcinfo->oi_nstored);
177+
178+
column->nstored = opcinfo->oi_nstored;
179+
for (i = 0; i < opcinfo->oi_nstored; i++)
170180
{
171-
Oid output;
172-
bool isVarlena;
173-
BrinOpcInfo *opcinfo;
174-
int i;
175-
brin_column_state *column;
176-
177-
opcinfo = state->bdesc->bd_info[attno - 1];
178-
column = palloc(offsetof(brin_column_state, outputFn) +
179-
sizeof(FmgrInfo) * opcinfo->oi_nstored);
180-
181-
column->nstored = opcinfo->oi_nstored;
182-
for (i = 0; i < opcinfo->oi_nstored; i++)
183-
{
184-
getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
185-
fmgr_info(output, &column->outputFn[i]);
186-
}
187-
188-
state->columns[attno - 1] = column;
181+
getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
182+
fmgr_info(output, &column->outputFn[i]);
189183
}
190184

191-
index_close(indexRel, AccessShareLock);
192-
193-
fctx->user_fctx = state;
194-
fctx->tuple_desc = BlessTupleDesc(tupdesc);
195-
196-
MemoryContextSwitchTo(mctx);
185+
columns[attno - 1] = column;
197186
}
198187

199-
fctx = SRF_PERCALL_SETUP();
200-
state = fctx->user_fctx;
201-
202-
if (!state->done)
188+
offset = FirstOffsetNumber;
189+
unusedItem = false;
190+
dtup = NULL;
191+
for (;;)
203192
{
204-
HeapTuple result;
205193
Datum values[7];
206194
bool nulls[7];
207195

@@ -211,39 +199,30 @@ brin_page_items(PG_FUNCTION_ARGS)
211199
* signal for obtaining and decoding the next one. If that's not the
212200
* case, we output the next attribute.
213201
*/
214-
if (state->dtup == NULL)
202+
if (dtup == NULL)
215203
{
216-
BrinTuple *tup;
217-
MemoryContext mctx;
218204
ItemId itemId;
219205

220-
/* deformed tuple must live across calls */
221-
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
222-
223206
/* verify item status: if there's no data, we can't decode */
224-
itemId = PageGetItemId(state->page, state->offset);
207+
itemId = PageGetItemId(page, offset);
225208
if (ItemIdIsUsed(itemId))
226209
{
227-
tup = (BrinTuple *) PageGetItem(state->page,
228-
PageGetItemId(state->page,
229-
state->offset));
230-
state->dtup = brin_deform_tuple(state->bdesc, tup);
231-
state->attno = 1;
232-
state->unusedItem = false;
210+
dtup = brin_deform_tuple(bdesc,
211+
(BrinTuple *) PageGetItem(page, itemId));
212+
attno = 1;
213+
unusedItem = false;
233214
}
234215
else
235-
state->unusedItem = true;
236-
237-
MemoryContextSwitchTo(mctx);
216+
unusedItem = true;
238217
}
239218
else
240-
state->attno++;
219+
attno++;
241220

242221
MemSet(nulls, 0, sizeof(nulls));
243222

244-
if (state->unusedItem)
223+
if (unusedItem)
245224
{
246-
values[0] = UInt16GetDatum(state->offset);
225+
values[0] = UInt16GetDatum(offset);
247226
nulls[1] = true;
248227
nulls[2] = true;
249228
nulls[3] = true;
@@ -253,17 +232,17 @@ brin_page_items(PG_FUNCTION_ARGS)
253232
}
254233
else
255234
{
256-
int att = state->attno - 1;
257-
258-
values[0] = UInt16GetDatum(state->offset);
259-
values[1] = UInt32GetDatum(state->dtup->bt_blkno);
260-
values[2] = UInt16GetDatum(state->attno);
261-
values[3] = BoolGetDatum(state->dtup->bt_columns[att].bv_allnulls);
262-
values[4] = BoolGetDatum(state->dtup->bt_columns[att].bv_hasnulls);
263-
values[5] = BoolGetDatum(state->dtup->bt_placeholder);
264-
if (!state->dtup->bt_columns[att].bv_allnulls)
235+
int att = attno - 1;
236+
237+
values[0] = UInt16GetDatum(offset);
238+
values[1] = UInt32GetDatum(dtup->bt_blkno);
239+
values[2] = UInt16GetDatum(attno);
240+
values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls);
241+
values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls);
242+
values[5] = BoolGetDatum(dtup->bt_placeholder);
243+
if (!dtup->bt_columns[att].bv_allnulls)
265244
{
266-
BrinValues *bvalues = &state->dtup->bt_columns[att];
245+
BrinValues *bvalues = &dtup->bt_columns[att];
267246
StringInfoData s;
268247
bool first;
269248
int i;
@@ -272,14 +251,14 @@ brin_page_items(PG_FUNCTION_ARGS)
272251
appendStringInfoChar(&s, '{');
273252

274253
first = true;
275-
for (i = 0; i < state->columns[att]->nstored; i++)
254+
for (i = 0; i < columns[att]->nstored; i++)
276255
{
277256
char *val;
278257

279258
if (!first)
280259
appendStringInfoString(&s, " .. ");
281260
first = false;
282-
val = OutputFunctionCall(&state->columns[att]->outputFn[i],
261+
val = OutputFunctionCall(&columns[att]->outputFn[i],
283262
bvalues->bv_values[i]);
284263
appendStringInfoString(&s, val);
285264
pfree(val);
@@ -295,35 +274,35 @@ brin_page_items(PG_FUNCTION_ARGS)
295274
}
296275
}
297276

298-
result = heap_form_tuple(fctx->tuple_desc, values, nulls);
277+
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
299278

300279
/*
301280
* If the item was unused, jump straight to the next one; otherwise,
302281
* the only cleanup needed here is to set our signal to go to the next
303282
* tuple in the following iteration, by freeing the current one.
304283
*/
305-
if (state->unusedItem)
306-
state->offset = OffsetNumberNext(state->offset);
307-
else if (state->attno >= state->bdesc->bd_tupdesc->natts)
284+
if (unusedItem)
285+
offset = OffsetNumberNext(offset);
286+
else if (attno >= bdesc->bd_tupdesc->natts)
308287
{
309-
pfree(state->dtup);
310-
state->dtup = NULL;
311-
state->offset = OffsetNumberNext(state->offset);
288+
pfree(dtup);
289+
dtup = NULL;
290+
offset = OffsetNumberNext(offset);
312291
}
313292

314293
/*
315-
* If we're beyond the end of the page, set flag to end the function
316-
* in the following iteration.
294+
* If we're beyond the end of the page, we're done.
317295
*/
318-
if (state->offset > PageGetMaxOffsetNumber(state->page))
319-
state->done = true;
320-
321-
SRF_RETURN_NEXT(fctx, HeapTupleGetDatum(result));
296+
if (offset > PageGetMaxOffsetNumber(page))
297+
break;
322298
}
323299

324-
brin_free_desc(state->bdesc);
300+
/* clean up and return the tuplestore */
301+
brin_free_desc(bdesc);
302+
tuplestore_donestoring(tupstore);
303+
index_close(indexRel, AccessShareLock);
325304

326-
SRF_RETURN_DONE(fctx);
305+
return (Datum) 0;
327306
}
328307

329308
Datum

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