Simplify SRFs using materialize mode in contrib/ modules
9e98583
introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors. This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.
Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall(). Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.
Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com
This commit is contained in:
parent
d5ed9da41d
commit
5b81703787
|
@ -165,7 +165,6 @@ static bool check_tuple_visibility(HeapCheckContext *ctx);
|
||||||
static void report_corruption(HeapCheckContext *ctx, char *msg);
|
static void report_corruption(HeapCheckContext *ctx, char *msg);
|
||||||
static void report_toast_corruption(HeapCheckContext *ctx,
|
static void report_toast_corruption(HeapCheckContext *ctx,
|
||||||
ToastedAttribute *ta, char *msg);
|
ToastedAttribute *ta, char *msg);
|
||||||
static TupleDesc verify_heapam_tupdesc(void);
|
|
||||||
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid,
|
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid,
|
||||||
const HeapCheckContext *ctx);
|
const HeapCheckContext *ctx);
|
||||||
static void update_cached_xid_range(HeapCheckContext *ctx);
|
static void update_cached_xid_range(HeapCheckContext *ctx);
|
||||||
|
@ -214,8 +213,6 @@ Datum
|
||||||
verify_heapam(PG_FUNCTION_ARGS)
|
verify_heapam(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
MemoryContext old_context;
|
|
||||||
bool random_access;
|
|
||||||
HeapCheckContext ctx;
|
HeapCheckContext ctx;
|
||||||
Buffer vmbuffer = InvalidBuffer;
|
Buffer vmbuffer = InvalidBuffer;
|
||||||
Oid relid;
|
Oid relid;
|
||||||
|
@ -227,16 +224,6 @@ verify_heapam(PG_FUNCTION_ARGS)
|
||||||
BlockNumber nblocks;
|
BlockNumber nblocks;
|
||||||
const char *skip;
|
const char *skip;
|
||||||
|
|
||||||
/* 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")));
|
|
||||||
|
|
||||||
/* Check supplied arguments */
|
/* Check supplied arguments */
|
||||||
if (PG_ARGISNULL(0))
|
if (PG_ARGISNULL(0))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -290,15 +277,10 @@ verify_heapam(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
ctx.attnum = -1;
|
ctx.attnum = -1;
|
||||||
|
|
||||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
/* Construct the tuplestore and tuple descriptor */
|
||||||
old_context = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
|
ctx.tupdesc = rsinfo->setDesc;
|
||||||
ctx.tupdesc = verify_heapam_tupdesc();
|
ctx.tupstore = rsinfo->setResult;
|
||||||
ctx.tupstore = tuplestore_begin_heap(random_access, false, work_mem);
|
|
||||||
rsinfo->returnMode = SFRM_Materialize;
|
|
||||||
rsinfo->setResult = ctx.tupstore;
|
|
||||||
rsinfo->setDesc = ctx.tupdesc;
|
|
||||||
MemoryContextSwitchTo(old_context);
|
|
||||||
|
|
||||||
/* Open relation, check relkind and access method */
|
/* Open relation, check relkind and access method */
|
||||||
ctx.rel = relation_open(relid, AccessShareLock);
|
ctx.rel = relation_open(relid, AccessShareLock);
|
||||||
|
@ -630,26 +612,6 @@ report_toast_corruption(HeapCheckContext *ctx, ToastedAttribute *ta,
|
||||||
ctx->is_corrupt = true;
|
ctx->is_corrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct the TupleDesc used to report messages about corruptions found
|
|
||||||
* while scanning the heap.
|
|
||||||
*/
|
|
||||||
static TupleDesc
|
|
||||||
verify_heapam_tupdesc(void)
|
|
||||||
{
|
|
||||||
TupleDesc tupdesc;
|
|
||||||
AttrNumber a = 0;
|
|
||||||
|
|
||||||
tupdesc = CreateTemplateTupleDesc(HEAPCHECK_RELATION_COLS);
|
|
||||||
TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
|
|
||||||
TupleDescInitEntry(tupdesc, ++a, "offnum", INT4OID, -1, 0);
|
|
||||||
TupleDescInitEntry(tupdesc, ++a, "attnum", INT4OID, -1, 0);
|
|
||||||
TupleDescInitEntry(tupdesc, ++a, "msg", TEXTOID, -1, 0);
|
|
||||||
Assert(a == HEAPCHECK_RELATION_COLS);
|
|
||||||
|
|
||||||
return BlessTupleDesc(tupdesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for tuple header corruption.
|
* Check for tuple header corruption.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1928,12 +1928,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
|
||||||
PGconn *conn;
|
PGconn *conn;
|
||||||
PGnotify *notify;
|
PGnotify *notify;
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
MemoryContext per_query_ctx;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
|
|
||||||
prepTuplestoreResult(fcinfo);
|
|
||||||
|
|
||||||
dblink_init();
|
dblink_init();
|
||||||
if (PG_NARGS() == 1)
|
if (PG_NARGS() == 1)
|
||||||
|
@ -1941,23 +1935,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
|
||||||
else
|
else
|
||||||
conn = pconn->conn;
|
conn = pconn->conn;
|
||||||
|
|
||||||
/* create the tuplestore in per-query memory */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
|
||||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
|
||||||
|
|
||||||
tupdesc = CreateTemplateTupleDesc(DBLINK_NOTIFY_COLS);
|
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "notify_name",
|
|
||||||
TEXTOID, -1, 0);
|
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "be_pid",
|
|
||||||
INT4OID, -1, 0);
|
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "extra",
|
|
||||||
TEXTOID, -1, 0);
|
|
||||||
|
|
||||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
rsinfo->setDesc = tupdesc;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
PQconsumeInput(conn);
|
PQconsumeInput(conn);
|
||||||
while ((notify = PQnotifies(conn)) != NULL)
|
while ((notify = PQnotifies(conn)) != NULL)
|
||||||
|
@ -1980,7 +1958,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
|
||||||
else
|
else
|
||||||
nulls[2] = true;
|
nulls[2] = true;
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
|
|
||||||
PQfreemem(notify);
|
PQfreemem(notify);
|
||||||
PQconsumeInput(conn);
|
PQconsumeInput(conn);
|
||||||
|
|
|
@ -126,9 +126,6 @@ brin_page_items(PG_FUNCTION_ARGS)
|
||||||
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
||||||
Oid indexRelid = PG_GETARG_OID(1);
|
Oid indexRelid = PG_GETARG_OID(1);
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
TupleDesc tupdesc;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
Relation indexRel;
|
Relation indexRel;
|
||||||
brin_column_state **columns;
|
brin_column_state **columns;
|
||||||
BrinDesc *bdesc;
|
BrinDesc *bdesc;
|
||||||
|
@ -143,29 +140,7 @@ brin_page_items(PG_FUNCTION_ARGS)
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to use raw page functions")));
|
errmsg("must be superuser to use raw page functions")));
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
|
||||||
|
|
||||||
/* Build a tuple descriptor for our result type */
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
/* Build tuplestore to hold the result rows */
|
|
||||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
|
||||||
|
|
||||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
|
||||||
rsinfo->returnMode = SFRM_Materialize;
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
rsinfo->setDesc = tupdesc;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
indexRel = index_open(indexRelid, AccessShareLock);
|
indexRel = index_open(indexRelid, AccessShareLock);
|
||||||
bdesc = brin_build_desc(indexRel);
|
bdesc = brin_build_desc(indexRel);
|
||||||
|
@ -251,7 +226,7 @@ brin_page_items(PG_FUNCTION_ARGS)
|
||||||
int att = attno - 1;
|
int att = attno - 1;
|
||||||
|
|
||||||
values[0] = UInt16GetDatum(offset);
|
values[0] = UInt16GetDatum(offset);
|
||||||
switch (TupleDescAttr(tupdesc, 1)->atttypid)
|
switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
|
||||||
{
|
{
|
||||||
case INT8OID:
|
case INT8OID:
|
||||||
values[1] = Int64GetDatum((int64) dtup->bt_blkno);
|
values[1] = Int64GetDatum((int64) dtup->bt_blkno);
|
||||||
|
@ -301,7 +276,7 @@ brin_page_items(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the item was unused, jump straight to the next one; otherwise,
|
* If the item was unused, jump straight to the next one; otherwise,
|
||||||
|
|
|
@ -97,10 +97,6 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
bool randomAccess;
|
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
Page page;
|
Page page;
|
||||||
OffsetNumber offset;
|
OffsetNumber offset;
|
||||||
OffsetNumber maxoff = InvalidOffsetNumber;
|
OffsetNumber maxoff = InvalidOffsetNumber;
|
||||||
|
@ -110,29 +106,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to use raw page functions")));
|
errmsg("must be superuser to use raw page functions")));
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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")));
|
|
||||||
|
|
||||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
|
||||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
|
||||||
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
page = get_page_from_raw(raw_page);
|
page = get_page_from_raw(raw_page);
|
||||||
|
|
||||||
|
@ -173,7 +147,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
||||||
values[3] = BoolGetDatum(ItemIdIsDead(id));
|
values[3] = BoolGetDatum(ItemIdIsDead(id));
|
||||||
values[4] = PointerGetDatum(tuple_bytea);
|
values[4] = PointerGetDatum(tuple_bytea);
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
|
@ -185,11 +159,7 @@ gist_page_items(PG_FUNCTION_ARGS)
|
||||||
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
||||||
Oid indexRelid = PG_GETARG_OID(1);
|
Oid indexRelid = PG_GETARG_OID(1);
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
bool randomAccess;
|
|
||||||
Relation indexRel;
|
Relation indexRel;
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
Page page;
|
Page page;
|
||||||
OffsetNumber offset;
|
OffsetNumber offset;
|
||||||
OffsetNumber maxoff = InvalidOffsetNumber;
|
OffsetNumber maxoff = InvalidOffsetNumber;
|
||||||
|
@ -199,29 +169,7 @@ gist_page_items(PG_FUNCTION_ARGS)
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
errmsg("must be superuser to use raw page functions")));
|
errmsg("must be superuser to use raw page functions")));
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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")));
|
|
||||||
|
|
||||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
|
||||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
|
||||||
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Open the relation */
|
/* Open the relation */
|
||||||
indexRel = index_open(indexRelid, AccessShareLock);
|
indexRel = index_open(indexRelid, AccessShareLock);
|
||||||
|
@ -272,7 +220,7 @@ gist_page_items(PG_FUNCTION_ARGS)
|
||||||
nulls[4] = true;
|
nulls[4] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
}
|
}
|
||||||
|
|
||||||
relation_close(indexRel, AccessShareLock);
|
relation_close(indexRel, AccessShareLock);
|
||||||
|
|
|
@ -1494,10 +1494,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||||
bool showtext)
|
bool showtext)
|
||||||
{
|
{
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
MemoryContext per_query_ctx;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
Oid userid = GetUserId();
|
Oid userid = GetUserId();
|
||||||
bool is_allowed_role = false;
|
bool is_allowed_role = false;
|
||||||
char *qbuffer = NULL;
|
char *qbuffer = NULL;
|
||||||
|
@ -1516,30 +1512,14 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
|
errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
|
||||||
|
|
||||||
/* Switch into long-lived context to construct returned data structures */
|
|
||||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
|
||||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
|
||||||
|
|
||||||
/* Build a tuple descriptor for our result type */
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check we have the expected number of output arguments. Aside from
|
* Check we have the expected number of output arguments. Aside from
|
||||||
* being a good safety check, we need a kluge here to detect API version
|
* being a good safety check, we need a kluge here to detect API version
|
||||||
* 1.1, which was wedged into the code in an ill-considered way.
|
* 1.1, which was wedged into the code in an ill-considered way.
|
||||||
*/
|
*/
|
||||||
switch (tupdesc->natts)
|
switch (rsinfo->setDesc->natts)
|
||||||
{
|
{
|
||||||
case PG_STAT_STATEMENTS_COLS_V1_0:
|
case PG_STAT_STATEMENTS_COLS_V1_0:
|
||||||
if (api_version != PGSS_V1_0)
|
if (api_version != PGSS_V1_0)
|
||||||
|
@ -1571,13 +1551,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||||
elog(ERROR, "incorrect number of output arguments");
|
elog(ERROR, "incorrect number of output arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
|
||||||
rsinfo->returnMode = SFRM_Materialize;
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
rsinfo->setDesc = tupdesc;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We'd like to load the query text file (if needed) while not holding any
|
* We'd like to load the query text file (if needed) while not holding any
|
||||||
* lock on pgss->lock. In the worst case we'll have to do this again
|
* lock on pgss->lock. In the worst case we'll have to do this again
|
||||||
|
@ -1800,7 +1773,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||||
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
|
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
|
||||||
-1 /* fail if you forget to update this assert */ ));
|
-1 /* fail if you forget to update this assert */ ));
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
}
|
}
|
||||||
|
|
||||||
LWLockRelease(pgss->lock);
|
LWLockRelease(pgss->lock);
|
||||||
|
|
|
@ -66,42 +66,16 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *relname = PG_GETARG_TEXT_PP(0);
|
text *relname = PG_GETARG_TEXT_PP(0);
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
bool randomAccess;
|
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
AttInMetadata *attinmeta;
|
AttInMetadata *attinmeta;
|
||||||
Relation rel;
|
Relation rel;
|
||||||
RangeVar *relrv;
|
RangeVar *relrv;
|
||||||
TableScanDesc scan;
|
TableScanDesc scan;
|
||||||
HeapScanDesc hscan;
|
HeapScanDesc hscan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
MemoryContext oldcontext;
|
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
char **values;
|
char **values;
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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")));
|
|
||||||
|
|
||||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
|
||||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
|
||||||
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Access the table */
|
/* Access the table */
|
||||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
||||||
|
@ -140,9 +114,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
||||||
scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
|
scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
|
||||||
hscan = (HeapScanDesc) scan;
|
hscan = (HeapScanDesc) scan;
|
||||||
|
|
||||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
|
||||||
|
|
||||||
values = (char **) palloc(tupdesc->natts * sizeof(char *));
|
values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
|
||||||
|
|
||||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||||
{
|
{
|
||||||
|
@ -288,7 +262,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
/* build a tuple */
|
/* build a tuple */
|
||||||
tuple = BuildTupleFromCStrings(attinmeta, values);
|
tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||||
tuplestore_puttuple(tupstore, tuple);
|
tuplestore_puttuple(rsinfo->setResult, tuple);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1661,37 +1661,10 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
#define POSTGRES_FDW_GET_CONNECTIONS_COLS 2
|
#define POSTGRES_FDW_GET_CONNECTIONS_COLS 2
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
TupleDesc tupdesc;
|
|
||||||
Tuplestorestate *tupstore;
|
|
||||||
MemoryContext per_query_ctx;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
HASH_SEQ_STATUS scan;
|
HASH_SEQ_STATUS scan;
|
||||||
ConnCacheEntry *entry;
|
ConnCacheEntry *entry;
|
||||||
|
|
||||||
/* check to see if caller supports us returning a tuplestore */
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
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_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
|
||||||
|
|
||||||
/* Build a tuple descriptor for our result type */
|
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
|
|
||||||
/* Build tuplestore to hold the result rows */
|
|
||||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
|
||||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
|
||||||
|
|
||||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
|
||||||
rsinfo->returnMode = SFRM_Materialize;
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
rsinfo->setDesc = tupdesc;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
/* If cache doesn't exist, we return no records */
|
/* If cache doesn't exist, we return no records */
|
||||||
if (!ConnectionHash)
|
if (!ConnectionHash)
|
||||||
|
@ -1757,7 +1730,7 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
values[1] = BoolGetDatum(!entry->invalidated);
|
values[1] = BoolGetDatum(!entry->invalidated);
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -491,15 +491,9 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
HeapTuple spi_tuple;
|
HeapTuple spi_tuple;
|
||||||
TupleDesc spi_tupdesc;
|
TupleDesc spi_tupdesc;
|
||||||
|
|
||||||
/* Output tuple (tuplestore) support */
|
|
||||||
Tuplestorestate *tupstore = NULL;
|
|
||||||
TupleDesc ret_tupdesc;
|
|
||||||
HeapTuple ret_tuple;
|
|
||||||
|
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
AttInMetadata *attinmeta;
|
AttInMetadata *attinmeta;
|
||||||
MemoryContext per_query_ctx;
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
|
|
||||||
char **values;
|
char **values;
|
||||||
xmlChar **xpaths;
|
xmlChar **xpaths;
|
||||||
|
@ -517,48 +511,10 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
PgXmlErrorContext *xmlerrcxt;
|
PgXmlErrorContext *xmlerrcxt;
|
||||||
volatile xmlDocPtr doctree = NULL;
|
volatile xmlDocPtr doctree = NULL;
|
||||||
|
|
||||||
/* We only have a valid tuple description in table function mode */
|
SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
|
||||||
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->expectedDesc == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("xpath_table must be called as a table function")));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We want to materialise because it means that we don't have to carry
|
|
||||||
* libxml2 parser state between invocations of this function
|
|
||||||
*/
|
|
||||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("xpath_table requires Materialize mode, but it is not "
|
|
||||||
"allowed in this context")));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The tuplestore must exist in a higher context than this function call
|
|
||||||
* (per_query_ctx is used)
|
|
||||||
*/
|
|
||||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
|
||||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the tuplestore - work_mem is the max in-memory size before a
|
|
||||||
* file is created on disk to hold it.
|
|
||||||
*/
|
|
||||||
tupstore =
|
|
||||||
tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
|
|
||||||
false, work_mem);
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
/* get the requested return tuple description */
|
|
||||||
ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
|
|
||||||
|
|
||||||
/* must have at least one output column (for the pkey) */
|
/* must have at least one output column (for the pkey) */
|
||||||
if (ret_tupdesc->natts < 1)
|
if (rsinfo->setDesc->natts < 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("xpath_table must have at least one output column")));
|
errmsg("xpath_table must have at least one output column")));
|
||||||
|
@ -571,14 +527,10 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
* representation.
|
* representation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
|
attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
|
||||||
|
|
||||||
/* Set return mode and allocate value space. */
|
values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
|
||||||
rsinfo->returnMode = SFRM_Materialize;
|
xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
|
||||||
rsinfo->setDesc = ret_tupdesc;
|
|
||||||
|
|
||||||
values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
|
|
||||||
xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split XPaths. xpathset is a writable CString.
|
* Split XPaths. xpathset is a writable CString.
|
||||||
|
@ -587,7 +539,7 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
numpaths = 0;
|
numpaths = 0;
|
||||||
pos = xpathset;
|
pos = xpathset;
|
||||||
while (numpaths < (ret_tupdesc->natts - 1))
|
while (numpaths < (rsinfo->setDesc->natts - 1))
|
||||||
{
|
{
|
||||||
xpaths[numpaths++] = (xmlChar *) pos;
|
xpaths[numpaths++] = (xmlChar *) pos;
|
||||||
pos = strstr(pos, pathsep);
|
pos = strstr(pos, pathsep);
|
||||||
|
@ -621,9 +573,6 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
tuptable = SPI_tuptable;
|
tuptable = SPI_tuptable;
|
||||||
spi_tupdesc = tuptable->tupdesc;
|
spi_tupdesc = tuptable->tupdesc;
|
||||||
|
|
||||||
/* Switch out of SPI context */
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that SPI returned correct result. If you put a comma into one of
|
* Check that SPI returned correct result. If you put a comma into one of
|
||||||
* the function parameters, this will catch it when the SPI query returns
|
* the function parameters, this will catch it when the SPI query returns
|
||||||
|
@ -655,6 +604,7 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
xmlXPathObjectPtr res;
|
xmlXPathObjectPtr res;
|
||||||
xmlChar *resstr;
|
xmlChar *resstr;
|
||||||
xmlXPathCompExprPtr comppath;
|
xmlXPathCompExprPtr comppath;
|
||||||
|
HeapTuple ret_tuple;
|
||||||
|
|
||||||
/* Extract the row data as C Strings */
|
/* Extract the row data as C Strings */
|
||||||
spi_tuple = tuptable->vals[i];
|
spi_tuple = tuptable->vals[i];
|
||||||
|
@ -666,7 +616,7 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
* return NULL in all columns. Note that this also means that
|
* return NULL in all columns. Note that this also means that
|
||||||
* spare columns will be NULL.
|
* spare columns will be NULL.
|
||||||
*/
|
*/
|
||||||
for (j = 0; j < ret_tupdesc->natts; j++)
|
for (j = 0; j < rsinfo->setDesc->natts; j++)
|
||||||
values[j] = NULL;
|
values[j] = NULL;
|
||||||
|
|
||||||
/* Insert primary key */
|
/* Insert primary key */
|
||||||
|
@ -682,7 +632,7 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
/* not well-formed, so output all-NULL tuple */
|
/* not well-formed, so output all-NULL tuple */
|
||||||
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||||
tuplestore_puttuple(tupstore, ret_tuple);
|
tuplestore_puttuple(rsinfo->setResult, ret_tuple);
|
||||||
heap_freetuple(ret_tuple);
|
heap_freetuple(ret_tuple);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -749,7 +699,7 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
if (had_values)
|
if (had_values)
|
||||||
{
|
{
|
||||||
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||||
tuplestore_puttuple(tupstore, ret_tuple);
|
tuplestore_puttuple(rsinfo->setResult, ret_tuple);
|
||||||
heap_freetuple(ret_tuple);
|
heap_freetuple(ret_tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,8 +735,6 @@ xpath_table(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
SPI_finish();
|
SPI_finish();
|
||||||
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SFRM_Materialize mode expects us to return a NULL Datum. The actual
|
* SFRM_Materialize mode expects us to return a NULL Datum. The actual
|
||||||
* tuples are in our tuplestore and passed back through rsinfo->setResult.
|
* tuples are in our tuplestore and passed back through rsinfo->setResult.
|
||||||
|
|
Loading…
Reference in New Issue