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

Commit 4d66285

Browse files
committed
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
1 parent e365a58 commit 4d66285

File tree

4 files changed

+51
-87
lines changed

4 files changed

+51
-87
lines changed

src/backend/statistics/mcv.c

+46-82
Original file line numberDiff line numberDiff line change
@@ -1248,9 +1248,6 @@ Datum
12481248
pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
12491249
{
12501250
FuncCallContext *funcctx;
1251-
int call_cntr;
1252-
int max_calls;
1253-
AttInMetadata *attinmeta;
12541251

12551252
/* stuff done only on the first call of the function */
12561253
if (SRF_IS_FIRSTCALL())
@@ -1280,125 +1277,92 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
12801277
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12811278
errmsg("function returning record called in context "
12821279
"that cannot accept type record")));
1280+
tupdesc = BlessTupleDesc(tupdesc);
12831281

12841282
/*
12851283
* generate attribute metadata needed later to produce tuples from raw
12861284
* C strings
12871285
*/
1288-
attinmeta = TupleDescGetAttInMetadata(tupdesc);
1289-
funcctx->attinmeta = attinmeta;
1286+
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
12901287

12911288
MemoryContextSwitchTo(oldcontext);
12921289
}
12931290

12941291
/* stuff done on every call of the function */
12951292
funcctx = SRF_PERCALL_SETUP();
12961293

1297-
call_cntr = funcctx->call_cntr;
1298-
max_calls = funcctx->max_calls;
1299-
attinmeta = funcctx->attinmeta;
1300-
1301-
if (call_cntr < max_calls) /* do when there is more left to send */
1294+
if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */
13021295
{
1303-
char **values;
1296+
Datum values[5];
1297+
bool nulls[5];
13041298
HeapTuple tuple;
13051299
Datum result;
1306-
1307-
StringInfoData itemValues;
1308-
StringInfoData itemNulls;
1300+
ArrayBuildState *astate_values = NULL;
1301+
ArrayBuildState *astate_nulls = NULL;
13091302

13101303
int i;
1311-
1312-
Oid *outfuncs;
1313-
FmgrInfo *fmgrinfo;
1314-
13151304
MCVList *mcvlist;
13161305
MCVItem *item;
13171306

13181307
mcvlist = (MCVList *) funcctx->user_fctx;
13191308

1320-
Assert(call_cntr < mcvlist->nitems);
1321-
1322-
item = &mcvlist->items[call_cntr];
1323-
1324-
/*
1325-
* Prepare a values array for building the returned tuple. This should
1326-
* be an array of C strings which will be processed later by the type
1327-
* input functions.
1328-
*/
1329-
values = (char **) palloc0(5 * sizeof(char *));
1330-
1331-
values[0] = (char *) palloc(64 * sizeof(char)); /* item index */
1332-
values[3] = (char *) palloc(64 * sizeof(char)); /* frequency */
1333-
values[4] = (char *) palloc(64 * sizeof(char)); /* base frequency */
1309+
Assert(funcctx->call_cntr < mcvlist->nitems);
13341310

1335-
outfuncs = (Oid *) palloc0(sizeof(Oid) * mcvlist->ndimensions);
1336-
fmgrinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * mcvlist->ndimensions);
1311+
item = &mcvlist->items[funcctx->call_cntr];
13371312

13381313
for (i = 0; i < mcvlist->ndimensions; i++)
13391314
{
1340-
bool isvarlena;
1341-
1342-
getTypeOutputInfo(mcvlist->types[i], &outfuncs[i], &isvarlena);
1343-
1344-
fmgr_info(outfuncs[i], &fmgrinfo[i]);
1345-
}
13461315

1347-
/* build the arrays of values / nulls */
1348-
initStringInfo(&itemValues);
1349-
initStringInfo(&itemNulls);
1316+
astate_nulls = accumArrayResult(astate_nulls,
1317+
BoolGetDatum(item->isnull[i]),
1318+
false,
1319+
BOOLOID,
1320+
CurrentMemoryContext);
13501321

1351-
appendStringInfoChar(&itemValues, '{');
1352-
appendStringInfoChar(&itemNulls, '{');
1353-
1354-
for (i = 0; i < mcvlist->ndimensions; i++)
1355-
{
1356-
Datum val,
1357-
valout;
1358-
1359-
if (i > 0)
1322+
if (!item->isnull[i])
13601323
{
1361-
appendStringInfoString(&itemValues, ", ");
1362-
appendStringInfoString(&itemNulls, ", ");
1324+
bool isvarlena;
1325+
Oid outfunc;
1326+
FmgrInfo fmgrinfo;
1327+
Datum val;
1328+
text *txt;
1329+
1330+
/* lookup output func for the type */
1331+
getTypeOutputInfo(mcvlist->types[i], &outfunc, &isvarlena);
1332+
fmgr_info(outfunc, &fmgrinfo);
1333+
1334+
val = FunctionCall1(&fmgrinfo, item->values[i]);
1335+
txt = cstring_to_text(DatumGetPointer(val));
1336+
1337+
astate_values = accumArrayResult(astate_values,
1338+
PointerGetDatum(txt),
1339+
false,
1340+
TEXTOID,
1341+
CurrentMemoryContext);
13631342
}
1364-
1365-
if (item->isnull[i])
1366-
valout = CStringGetDatum("NULL");
13671343
else
1368-
{
1369-
val = item->values[i];
1370-
valout = FunctionCall1(&fmgrinfo[i], val);
1371-
}
1372-
1373-
appendStringInfoString(&itemValues, DatumGetCString(valout));
1374-
appendStringInfoString(&itemNulls, item->isnull[i] ? "t" : "f");
1344+
astate_values = accumArrayResult(astate_values,
1345+
(Datum) 0,
1346+
true,
1347+
TEXTOID,
1348+
CurrentMemoryContext);
13751349
}
13761350

1377-
appendStringInfoChar(&itemValues, '}');
1378-
appendStringInfoChar(&itemNulls, '}');
1379-
1380-
snprintf(values[0], 64, "%d", call_cntr);
1381-
snprintf(values[3], 64, "%f", item->frequency);
1382-
snprintf(values[4], 64, "%f", item->base_frequency);
1351+
values[0] = Int32GetDatum(funcctx->call_cntr);
1352+
values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
1353+
values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
1354+
values[3] = Float8GetDatum(item->frequency);
1355+
values[4] = Float8GetDatum(item->base_frequency);
13831356

1384-
values[1] = itemValues.data;
1385-
values[2] = itemNulls.data;
1357+
/* no NULLs in the tuple */
1358+
memset(nulls, 0, sizeof(nulls));
13861359

13871360
/* build a tuple */
1388-
tuple = BuildTupleFromCStrings(attinmeta, values);
1361+
tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls);
13891362

13901363
/* make the tuple into a datum */
13911364
result = HeapTupleGetDatum(tuple);
13921365

1393-
/* clean up (this is not really necessary) */
1394-
pfree(itemValues.data);
1395-
pfree(itemNulls.data);
1396-
1397-
pfree(values[0]);
1398-
pfree(values[3]);
1399-
pfree(values[4]);
1400-
pfree(values);
1401-
14021366
SRF_RETURN_NEXT(funcctx, result);
14031367
}
14041368
else /* do when there is no more left */

src/include/catalog/catversion.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201907041
56+
#define CATALOG_VERSION_NO 201907051
5757

5858
#endif

src/include/catalog/pg_proc.dat

+1-1
Original file line numberDiff line numberDiff line change
@@ -5019,7 +5019,7 @@
50195019
{ oid => '3427', descr => 'details about MCV list items',
50205020
proname => 'pg_mcv_list_items', prorows => '1000', proretset => 't',
50215021
provolatile => 's', prorettype => 'record', proargtypes => 'pg_mcv_list',
5022-
proallargtypes => '{pg_mcv_list,int4,text,_bool,float8,float8}',
5022+
proallargtypes => '{pg_mcv_list,int4,_text,_bool,float8,float8}',
50235023
proargmodes => '{i,o,o,o,o,o}',
50245024
proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}',
50255025
prosrc => 'pg_stats_ext_mcvlist_items' },

src/test/regress/expected/stats_ext.out

+3-3
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,9 @@ SELECT m.*
614614
pg_mcv_list_items(d.stxdmcv) m
615615
WHERE s.stxname = 'mcv_lists_stats'
616616
AND d.stxoid = s.oid;
617-
index | values | nulls | frequency | base_frequency
618-
-------+-----------+---------+-----------+----------------
619-
0 | {1, 2, 3} | {f,f,f} | 1 | 1
617+
index | values | nulls | frequency | base_frequency
618+
-------+---------+---------+-----------+----------------
619+
0 | {1,2,3} | {f,f,f} | 1 | 1
620620
(1 row)
621621

622622
-- mcv with arrays

0 commit comments

Comments
 (0)