From 9aa3ce9bfdcab50a6d5dcf289bcf715e4a8aed9c Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Mon, 24 Jun 2019 00:50:53 +0200 Subject: [PATCH 1/3] rework pg_mcv_list_items --- src/backend/statistics/mcv.c | 106 ++++++++++++-------------------- src/include/catalog/pg_proc.dat | 2 +- 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c index 5fe61ea0a4..1b1a5c9846 100644 --- a/src/backend/statistics/mcv.c +++ b/src/backend/statistics/mcv.c @@ -1134,9 +1134,6 @@ Datum pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - int call_cntr; - int max_calls; - AttInMetadata *attinmeta; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) @@ -1166,13 +1163,13 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); + tupdesc = BlessTupleDesc(tupdesc); /* * generate attribute metadata needed later to produce tuples from raw * C strings */ - attinmeta = TupleDescGetAttInMetadata(tupdesc); - funcctx->attinmeta = attinmeta; + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); } @@ -1180,18 +1177,17 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); - call_cntr = funcctx->call_cntr; - max_calls = funcctx->max_calls; - attinmeta = funcctx->attinmeta; - - if (call_cntr < max_calls) /* do when there is more left to send */ + if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */ { - char **values; + Datum values[5]; + bool nulls[5]; HeapTuple tuple; Datum result; + int dims[1]; + int lbs[1]; - StringInfoData itemValues; - StringInfoData itemNulls; + Datum *itemNulls; + Datum *itemValues; int i; @@ -1203,24 +1199,16 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) mcvlist = (MCVList *) funcctx->user_fctx; - Assert(call_cntr < mcvlist->nitems); - - item = &mcvlist->items[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 *)); + Assert(funcctx->call_cntr < mcvlist->nitems); - 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 */ + item = &mcvlist->items[funcctx->call_cntr]; outfuncs = (Oid *) palloc0(sizeof(Oid) * mcvlist->ndimensions); fmgrinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * mcvlist->ndimensions); + itemValues = (Datum *) palloc(sizeof(Datum) * mcvlist->ndimensions); + itemNulls = (Datum *) palloc(sizeof(Datum) * mcvlist->ndimensions); + for (i = 0; i < mcvlist->ndimensions; i++) { bool isvarlena; @@ -1230,61 +1218,49 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS) fmgr_info(outfuncs[i], &fmgrinfo[i]); } - /* 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, ", "); - } + Datum valout; if (item->isnull[i]) - valout = CStringGetDatum("NULL"); + valout = (Datum) 0; else - { - val = item->values[i]; - valout = FunctionCall1(&fmgrinfo[i], val); - } + valout = FunctionCall1(&fmgrinfo[i], item->values[i]); - appendStringInfoString(&itemValues, DatumGetCString(valout)); - appendStringInfoString(&itemNulls, item->isnull[i] ? "t" : "f"); + itemNulls[i] = BoolGetDatum(item->isnull[i]); + itemValues[i] = valout; } - appendStringInfoChar(&itemValues, '}'); - appendStringInfoChar(&itemNulls, '}'); + dims[0] = mcvlist->ndimensions; + lbs[0] = 1; + + values[0] = Int32GetDatum(funcctx->call_cntr); - snprintf(values[0], 64, "%d", call_cntr); - snprintf(values[3], 64, "%f", item->frequency); - snprintf(values[4], 64, "%f", item->base_frequency); + values[1] = PointerGetDatum(construct_md_array(itemValues, + item->isnull, + 1, + dims, + lbs, + CSTRINGOID, -2, + false, 'c')); - values[1] = itemValues.data; - values[2] = itemNulls.data; + values[2] = PointerGetDatum(construct_array(itemNulls, + mcvlist->ndimensions, + BOOLOID, sizeof(bool), + true, 'c')); + + values[3] = Float8GetDatum(item->frequency); + values[4] = Float8GetDatum(item->base_frequency); + + /* no NULLs in the tuple */ + memset(nulls, 0, sizeof(nulls)); /* build a tuple */ - tuple = BuildTupleFromCStrings(attinmeta, values); + tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls); /* make the tuple into a datum */ 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); } else /* do when there is no more left */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..36ae2ac9d8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5019,7 +5019,7 @@ { oid => '3427', descr => 'details about MCV list items', proname => 'pg_mcv_list_items', prorows => '1000', proretset => 't', 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}', proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}', prosrc => 'pg_stats_ext_mcvlist_items' }, -- 2.20.1