Fix pg_mcv_list_items() to produce text[]
The function pg_mcv_list_items() returns values stored in MCV items. The items may contain columns with different data types, so the function was generating text array-like representation, but in an ad-hoc way without properly escaping various characters etc. Fixed by simply building a text[] array, which also makes it easier to use from queries etc. Requires changes to pg_proc entry, so bump catversion. Backpatch to 12, where multi-column MCV lists were introduced. Author: Tomas Vondra Reviewed-by: Dean Rasheed Discussion: https://postgr.es/m/20190618205920.qtlzcu73whfpfqne@development
This commit is contained in:
parent
e365a581c2
commit
4d66285adc
@ -1248,9 +1248,6 @@ Datum
|
|||||||
pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
|
pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
FuncCallContext *funcctx;
|
FuncCallContext *funcctx;
|
||||||
int call_cntr;
|
|
||||||
int max_calls;
|
|
||||||
AttInMetadata *attinmeta;
|
|
||||||
|
|
||||||
/* stuff done only on the first call of the function */
|
/* stuff done only on the first call of the function */
|
||||||
if (SRF_IS_FIRSTCALL())
|
if (SRF_IS_FIRSTCALL())
|
||||||
@ -1280,13 +1277,13 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("function returning record called in context "
|
errmsg("function returning record called in context "
|
||||||
"that cannot accept type record")));
|
"that cannot accept type record")));
|
||||||
|
tupdesc = BlessTupleDesc(tupdesc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* generate attribute metadata needed later to produce tuples from raw
|
* generate attribute metadata needed later to produce tuples from raw
|
||||||
* C strings
|
* C strings
|
||||||
*/
|
*/
|
||||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||||
funcctx->attinmeta = attinmeta;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
@ -1294,111 +1291,78 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
|
|||||||
/* stuff done on every call of the function */
|
/* stuff done on every call of the function */
|
||||||
funcctx = SRF_PERCALL_SETUP();
|
funcctx = SRF_PERCALL_SETUP();
|
||||||
|
|
||||||
call_cntr = funcctx->call_cntr;
|
if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */
|
||||||
max_calls = funcctx->max_calls;
|
|
||||||
attinmeta = funcctx->attinmeta;
|
|
||||||
|
|
||||||
if (call_cntr < max_calls) /* do when there is more left to send */
|
|
||||||
{
|
{
|
||||||
char **values;
|
Datum values[5];
|
||||||
|
bool nulls[5];
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Datum result;
|
Datum result;
|
||||||
|
ArrayBuildState *astate_values = NULL;
|
||||||
StringInfoData itemValues;
|
ArrayBuildState *astate_nulls = NULL;
|
||||||
StringInfoData itemNulls;
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
Oid *outfuncs;
|
|
||||||
FmgrInfo *fmgrinfo;
|
|
||||||
|
|
||||||
MCVList *mcvlist;
|
MCVList *mcvlist;
|
||||||
MCVItem *item;
|
MCVItem *item;
|
||||||
|
|
||||||
mcvlist = (MCVList *) funcctx->user_fctx;
|
mcvlist = (MCVList *) funcctx->user_fctx;
|
||||||
|
|
||||||
Assert(call_cntr < mcvlist->nitems);
|
Assert(funcctx->call_cntr < mcvlist->nitems);
|
||||||
|
|
||||||
item = &mcvlist->items[call_cntr];
|
item = &mcvlist->items[funcctx->call_cntr];
|
||||||
|
|
||||||
/*
|
|
||||||
* Prepare a values array for building the returned tuple. This should
|
|
||||||
* be an array of C strings which will be processed later by the type
|
|
||||||
* input functions.
|
|
||||||
*/
|
|
||||||
values = (char **) palloc0(5 * sizeof(char *));
|
|
||||||
|
|
||||||
values[0] = (char *) palloc(64 * sizeof(char)); /* item index */
|
|
||||||
values[3] = (char *) palloc(64 * sizeof(char)); /* frequency */
|
|
||||||
values[4] = (char *) palloc(64 * sizeof(char)); /* base frequency */
|
|
||||||
|
|
||||||
outfuncs = (Oid *) palloc0(sizeof(Oid) * mcvlist->ndimensions);
|
|
||||||
fmgrinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * mcvlist->ndimensions);
|
|
||||||
|
|
||||||
for (i = 0; i < mcvlist->ndimensions; i++)
|
for (i = 0; i < mcvlist->ndimensions; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
astate_nulls = accumArrayResult(astate_nulls,
|
||||||
|
BoolGetDatum(item->isnull[i]),
|
||||||
|
false,
|
||||||
|
BOOLOID,
|
||||||
|
CurrentMemoryContext);
|
||||||
|
|
||||||
|
if (!item->isnull[i])
|
||||||
{
|
{
|
||||||
bool isvarlena;
|
bool isvarlena;
|
||||||
|
Oid outfunc;
|
||||||
|
FmgrInfo fmgrinfo;
|
||||||
|
Datum val;
|
||||||
|
text *txt;
|
||||||
|
|
||||||
getTypeOutputInfo(mcvlist->types[i], &outfuncs[i], &isvarlena);
|
/* lookup output func for the type */
|
||||||
|
getTypeOutputInfo(mcvlist->types[i], &outfunc, &isvarlena);
|
||||||
|
fmgr_info(outfunc, &fmgrinfo);
|
||||||
|
|
||||||
fmgr_info(outfuncs[i], &fmgrinfo[i]);
|
val = FunctionCall1(&fmgrinfo, item->values[i]);
|
||||||
|
txt = cstring_to_text(DatumGetPointer(val));
|
||||||
|
|
||||||
|
astate_values = accumArrayResult(astate_values,
|
||||||
|
PointerGetDatum(txt),
|
||||||
|
false,
|
||||||
|
TEXTOID,
|
||||||
|
CurrentMemoryContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build the arrays of values / nulls */
|
|
||||||
initStringInfo(&itemValues);
|
|
||||||
initStringInfo(&itemNulls);
|
|
||||||
|
|
||||||
appendStringInfoChar(&itemValues, '{');
|
|
||||||
appendStringInfoChar(&itemNulls, '{');
|
|
||||||
|
|
||||||
for (i = 0; i < mcvlist->ndimensions; i++)
|
|
||||||
{
|
|
||||||
Datum val,
|
|
||||||
valout;
|
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&itemValues, ", ");
|
|
||||||
appendStringInfoString(&itemNulls, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->isnull[i])
|
|
||||||
valout = CStringGetDatum("NULL");
|
|
||||||
else
|
else
|
||||||
{
|
astate_values = accumArrayResult(astate_values,
|
||||||
val = item->values[i];
|
(Datum) 0,
|
||||||
valout = FunctionCall1(&fmgrinfo[i], val);
|
true,
|
||||||
|
TEXTOID,
|
||||||
|
CurrentMemoryContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
appendStringInfoString(&itemValues, DatumGetCString(valout));
|
values[0] = Int32GetDatum(funcctx->call_cntr);
|
||||||
appendStringInfoString(&itemNulls, item->isnull[i] ? "t" : "f");
|
values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
|
||||||
}
|
values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
|
||||||
|
values[3] = Float8GetDatum(item->frequency);
|
||||||
|
values[4] = Float8GetDatum(item->base_frequency);
|
||||||
|
|
||||||
appendStringInfoChar(&itemValues, '}');
|
/* no NULLs in the tuple */
|
||||||
appendStringInfoChar(&itemNulls, '}');
|
memset(nulls, 0, sizeof(nulls));
|
||||||
|
|
||||||
snprintf(values[0], 64, "%d", call_cntr);
|
|
||||||
snprintf(values[3], 64, "%f", item->frequency);
|
|
||||||
snprintf(values[4], 64, "%f", item->base_frequency);
|
|
||||||
|
|
||||||
values[1] = itemValues.data;
|
|
||||||
values[2] = itemNulls.data;
|
|
||||||
|
|
||||||
/* build a tuple */
|
/* build a tuple */
|
||||||
tuple = BuildTupleFromCStrings(attinmeta, values);
|
tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls);
|
||||||
|
|
||||||
/* make the tuple into a datum */
|
/* make the tuple into a datum */
|
||||||
result = HeapTupleGetDatum(tuple);
|
result = HeapTupleGetDatum(tuple);
|
||||||
|
|
||||||
/* clean up (this is not really necessary) */
|
|
||||||
pfree(itemValues.data);
|
|
||||||
pfree(itemNulls.data);
|
|
||||||
|
|
||||||
pfree(values[0]);
|
|
||||||
pfree(values[3]);
|
|
||||||
pfree(values[4]);
|
|
||||||
pfree(values);
|
|
||||||
|
|
||||||
SRF_RETURN_NEXT(funcctx, result);
|
SRF_RETURN_NEXT(funcctx, result);
|
||||||
}
|
}
|
||||||
else /* do when there is no more left */
|
else /* do when there is no more left */
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201907041
|
#define CATALOG_VERSION_NO 201907051
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -5019,7 +5019,7 @@
|
|||||||
{ oid => '3427', descr => 'details about MCV list items',
|
{ oid => '3427', descr => 'details about MCV list items',
|
||||||
proname => 'pg_mcv_list_items', prorows => '1000', proretset => 't',
|
proname => 'pg_mcv_list_items', prorows => '1000', proretset => 't',
|
||||||
provolatile => 's', prorettype => 'record', proargtypes => 'pg_mcv_list',
|
provolatile => 's', prorettype => 'record', proargtypes => 'pg_mcv_list',
|
||||||
proallargtypes => '{pg_mcv_list,int4,text,_bool,float8,float8}',
|
proallargtypes => '{pg_mcv_list,int4,_text,_bool,float8,float8}',
|
||||||
proargmodes => '{i,o,o,o,o,o}',
|
proargmodes => '{i,o,o,o,o,o}',
|
||||||
proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}',
|
proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}',
|
||||||
prosrc => 'pg_stats_ext_mcvlist_items' },
|
prosrc => 'pg_stats_ext_mcvlist_items' },
|
||||||
|
@ -615,8 +615,8 @@ SELECT m.*
|
|||||||
WHERE s.stxname = 'mcv_lists_stats'
|
WHERE s.stxname = 'mcv_lists_stats'
|
||||||
AND d.stxoid = s.oid;
|
AND d.stxoid = s.oid;
|
||||||
index | values | nulls | frequency | base_frequency
|
index | values | nulls | frequency | base_frequency
|
||||||
-------+-----------+---------+-----------+----------------
|
-------+---------+---------+-----------+----------------
|
||||||
0 | {1, 2, 3} | {f,f,f} | 1 | 1
|
0 | {1,2,3} | {f,f,f} | 1 | 1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- mcv with arrays
|
-- mcv with arrays
|
||||||
|
Loading…
x
Reference in New Issue
Block a user