Separate the memory allocation context for tsl_finalize_agg

Allocate the internal data structures in different memory contexts based on
the lifetime i.e. global to query or global to the group used by the finalize_agg query. This helps minimize lookups for global lifetime objects like Oids.
This commit is contained in:
gayyappan 2019-04-22 14:02:03 -04:00 committed by Matvey Arye
parent e034978eef
commit 58601cc3a0

View File

@ -76,9 +76,14 @@ typedef struct FACombineFnMeta
Oid combinefnoid; Oid combinefnoid;
Oid deserialfnoid; Oid deserialfnoid;
Oid transtype; Oid transtype;
Oid recv_fn;
Oid typIOParam;
FmgrInfo deserialfn; FmgrInfo deserialfn;
FmgrInfo internal_deserialfn;
FmgrInfo combinefn; FmgrInfo combinefn;
/* either deserialfn_fcinfo or internal_deserialfn_fcinfo is valid*/
FunctionCallInfoData deserialfn_fcinfo; FunctionCallInfoData deserialfn_fcinfo;
FunctionCallInfoData internal_deserialfn_fcinfo;
FunctionCallInfoData combfn_fcinfo; FunctionCallInfoData combfn_fcinfo;
} FACombineFnMeta; } FACombineFnMeta;
@ -104,12 +109,17 @@ typedef struct FAPerGroupState
bool trans_value_initialized; bool trans_value_initialized;
} FAPerGroupState; } FAPerGroupState;
/* metadata information that is common for the entire query */
typedef struct FAPerQueryState
{
FACombineFnMeta combine_meta;
FAFinalFnMeta final_meta;
} FAPerQueryState;
typedef struct FATransitionState typedef struct FATransitionState
{ {
FACombineFnMeta *combine_meta; FAPerQueryState *per_query_state;
FAFinalFnMeta *final_meta;
FAPerGroupState *per_group_state; FAPerGroupState *per_group_state;
} FATransitionState; } FATransitionState;
static Oid static Oid
@ -162,27 +172,29 @@ inner_agg_deserialize(FACombineFnMeta *combine_meta, bytea *serialized_partial,
} }
deser_fcinfo->arg[0] = PointerGetDatum(serialized_partial); deser_fcinfo->arg[0] = PointerGetDatum(serialized_partial);
deser_fcinfo->argnull[0] = serialized_isnull; deser_fcinfo->argnull[0] = serialized_isnull;
combine_meta->deserialfn_fcinfo.isnull = false;
deserialized = FunctionCallInvoke(deser_fcinfo); deserialized = FunctionCallInvoke(deser_fcinfo);
*deserialized_isnull = deser_fcinfo->isnull; *deserialized_isnull = deser_fcinfo->isnull;
} }
else if (!serialized_isnull) else if (!serialized_isnull)
{ {
int32 typmod = -1;
StringInfo string = makeStringInfo(); StringInfo string = makeStringInfo();
Oid recv_fn, typIOParam;
getTypeBinaryInputInfo(combine_meta->transtype, &recv_fn, &typIOParam);
appendBinaryStringInfo(string, appendBinaryStringInfo(string,
VARDATA_ANY(serialized_partial), VARDATA_ANY(serialized_partial),
VARSIZE_ANY_EXHDR(serialized_partial)); VARSIZE_ANY_EXHDR(serialized_partial));
/* combine_meta->internal_deserialfn_fcinfo.arg[0] = PointerGetDatum(string);
* Note that we may want to switch this to ReceiveFunctionCall at some combine_meta->internal_deserialfn_fcinfo.arg[1] =
* point in the future because OidReceiveFunctionCall puts a lot of ObjectIdGetDatum(combine_meta->typIOParam);
* stuff into CurrentMemoryContext that we may eventually want to manage combine_meta->internal_deserialfn_fcinfo.arg[2] = Int32GetDatum(typmod);
* ourselves. combine_meta->internal_deserialfn_fcinfo.argnull[0] = false;
*/ combine_meta->internal_deserialfn_fcinfo.argnull[1] = false;
deserialized = OidReceiveFunctionCall(recv_fn, string, typIOParam, 0); combine_meta->internal_deserialfn_fcinfo.argnull[2] = false;
*deserialized_isnull = false; combine_meta->internal_deserialfn_fcinfo.isnull = false;
deserialized = FunctionCallInvoke(&combine_meta->internal_deserialfn_fcinfo);
*deserialized_isnull = combine_meta->internal_deserialfn_fcinfo.isnull;
} }
PG_RETURN_DATUM(deserialized); PG_RETURN_DATUM(deserialized);
} }
@ -249,21 +261,36 @@ get_input_types(ArrayType *input_types, size_t *number_types)
}; };
static FATransitionState * static FATransitionState *
fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid collation, fa_transition_state_init(MemoryContext *fa_context, FAPerQueryState *qstate, AggState *fa_aggstate)
AggState *fa_aggstate, ArrayType *input_types)
{ {
FATransitionState *tstate = NULL; FATransitionState *tstate = NULL;
HeapTuple inner_agg_tuple;
Form_pg_aggregate inner_agg_form;
tstate = (FATransitionState *) MemoryContextAlloc(*fa_context, sizeof(*tstate)); tstate = (FATransitionState *) MemoryContextAlloc(*fa_context, sizeof(*tstate));
tstate->combine_meta = tstate->per_query_state = qstate;
(FACombineFnMeta *) MemoryContextAlloc(*fa_context, sizeof(*tstate->combine_meta));
tstate->final_meta =
(FAFinalFnMeta *) MemoryContextAlloc(*fa_context, sizeof(*tstate->final_meta));
tstate->per_group_state = tstate->per_group_state =
(FAPerGroupState *) MemoryContextAlloc(*fa_context, sizeof(*tstate->per_group_state)); (FAPerGroupState *) MemoryContextAlloc(*fa_context, sizeof(*tstate->per_group_state));
/* Need to init tstate->per_group_state->trans_value */
tstate->per_group_state->trans_value_isnull = true;
tstate->per_group_state->trans_value_initialized = false;
return tstate;
}
static FAPerQueryState *
fa_perquery_state_init(FunctionCallInfo fcinfo)
{
char *inner_agg_input_coll_schema = PG_ARGISNULL(2) ? NULL : NameStr(*PG_GETARG_NAME(2));
char *inner_agg_input_coll_name = PG_ARGISNULL(3) ? NULL : NameStr(*PG_GETARG_NAME(3));
ArrayType *input_types = PG_ARGISNULL(4) ? NULL : PG_GETARG_ARRAYTYPE_P(4);
Oid inner_agg_fn_oid = aggfnoid_from_aggname(PG_GETARG_TEXT_PP(1));
Oid collation = collation_oid_from_name(inner_agg_input_coll_schema, inner_agg_input_coll_name);
FAPerQueryState *tstate;
HeapTuple inner_agg_tuple;
Form_pg_aggregate inner_agg_form;
MemoryContext qcontext = fcinfo->flinfo->fn_mcxt;
MemoryContext oldcontext = MemoryContextSwitchTo(qcontext);
AggState *fa_aggstate = (AggState *) fcinfo->context;
/* look up catalog entry and populate what we need */ /* look up catalog entry and populate what we need */
inner_agg_tuple = SearchSysCache1(AGGFNOID, inner_agg_fn_oid); inner_agg_tuple = SearchSysCache1(AGGFNOID, inner_agg_fn_oid);
if (!HeapTupleIsValid(inner_agg_tuple)) if (!HeapTupleIsValid(inner_agg_tuple))
@ -274,40 +301,59 @@ fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid co
if (inner_agg_form->aggnumdirectargs != 0) if (inner_agg_form->aggnumdirectargs != 0)
elog(ERROR, elog(ERROR,
"function calls with direct args are not supported by TimescaleDB finalize agg"); "function calls with direct args are not supported by TimescaleDB finalize agg");
tstate = (FAPerQueryState *) MemoryContextAlloc(qcontext, sizeof(FAPerQueryState));
tstate->final_meta->finalfnoid = inner_agg_form->aggfinalfn; tstate->final_meta.finalfnoid = inner_agg_form->aggfinalfn;
tstate->combine_meta->combinefnoid = inner_agg_form->aggcombinefn; tstate->combine_meta.combinefnoid = inner_agg_form->aggcombinefn;
tstate->combine_meta->deserialfnoid = inner_agg_form->aggdeserialfn; tstate->combine_meta.deserialfnoid = inner_agg_form->aggdeserialfn;
tstate->combine_meta->transtype = inner_agg_form->aggtranstype; tstate->combine_meta.transtype = inner_agg_form->aggtranstype;
ReleaseSysCache(inner_agg_tuple); ReleaseSysCache(inner_agg_tuple);
/* initialize combine specific state, both the deserialize function and combine function */ /* initialize combine specific state, both the deserialize function and combine function */
if (!OidIsValid(tstate->combine_meta->combinefnoid)) if (!OidIsValid(tstate->combine_meta.combinefnoid))
elog(ERROR, elog(ERROR,
"no valid combine function for the aggregate specified in Timescale finalize call"); "no valid combine function for the aggregate specified in Timescale finalize call");
fmgr_info(tstate->combine_meta->combinefnoid, &tstate->combine_meta->combinefn); fmgr_info_cxt(tstate->combine_meta.combinefnoid, &tstate->combine_meta.combinefn, qcontext);
InitFunctionCallInfoData(tstate->combine_meta->combfn_fcinfo, InitFunctionCallInfoData(tstate->combine_meta.combfn_fcinfo,
&tstate->combine_meta->combinefn, &tstate->combine_meta.combinefn,
2, /* combine fn always has two args */ 2, /* combine fn always has two args */
collation, collation,
(void *) fa_aggstate, (void *) fa_aggstate,
NULL); NULL);
if (OidIsValid(tstate->combine_meta->deserialfnoid)) /* deserial fn not necessary, no need to if (OidIsValid(tstate->combine_meta.deserialfnoid)) /* deserial fn not necessary, no need to
throw errors if not found */ throw errors if not found */
{ {
fmgr_info(tstate->combine_meta->deserialfnoid, &tstate->combine_meta->deserialfn); fmgr_info_cxt(tstate->combine_meta.deserialfnoid,
InitFunctionCallInfoData(tstate->combine_meta->deserialfn_fcinfo, &tstate->combine_meta.deserialfn,
&tstate->combine_meta->deserialfn, qcontext);
InitFunctionCallInfoData(tstate->combine_meta.deserialfn_fcinfo,
&tstate->combine_meta.deserialfn,
1, /* deserialize always has 1 arg */ 1, /* deserialize always has 1 arg */
collation, collation,
(void *) fa_aggstate, (void *) fa_aggstate,
NULL); NULL);
} }
else
{
/* save information for internal deserialization. caching instead
of calling ReceiveFunctionCall */
getTypeBinaryInputInfo(tstate->combine_meta.transtype,
&tstate->combine_meta.recv_fn,
&tstate->combine_meta.typIOParam);
fmgr_info_cxt(tstate->combine_meta.recv_fn,
&tstate->combine_meta.internal_deserialfn,
qcontext);
InitFunctionCallInfoData(tstate->combine_meta.internal_deserialfn_fcinfo,
&tstate->combine_meta.internal_deserialfn,
3,
InvalidOid,
NULL,
NULL);
}
/* initialize finalfn specific state */ /* initialize finalfn specific state */
if (OidIsValid(tstate->final_meta->finalfnoid)) if (OidIsValid(tstate->final_meta.finalfnoid))
{ {
int num_args = 1; int num_args = 1;
Oid *types = NULL; Oid *types = NULL;
@ -317,13 +363,13 @@ fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid co
types = get_input_types(input_types, &number_types); types = get_input_types(input_types, &number_types);
num_args += number_types; num_args += number_types;
} }
if (num_args != get_func_nargs(tstate->final_meta->finalfnoid)) if (num_args != get_func_nargs(tstate->final_meta.finalfnoid))
elog(ERROR, "invalid number of input types"); elog(ERROR, "invalid number of input types");
fmgr_info(tstate->final_meta->finalfnoid, &tstate->final_meta->finalfn); fmgr_info_cxt(tstate->final_meta.finalfnoid, &tstate->final_meta.finalfn, qcontext);
/* pass the aggstate information from our current call context */ /* pass the aggstate information from our current call context */
InitFunctionCallInfoData(tstate->final_meta->finalfn_fcinfo, InitFunctionCallInfoData(tstate->final_meta.finalfn_fcinfo,
&tstate->final_meta->finalfn, &tstate->final_meta.finalfn,
num_args, num_args,
collation, collation,
(void *) fa_aggstate, (void *) fa_aggstate,
@ -337,20 +383,20 @@ fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid co
inner_agg_form->aggtranstype, inner_agg_form->aggtranstype,
types[number_types - 1], types[number_types - 1],
collation, collation,
tstate->final_meta->finalfnoid, tstate->final_meta.finalfnoid,
&expr); &expr);
fmgr_info_set_expr((Node *) expr, &tstate->final_meta->finalfn); fmgr_info_set_expr((Node *) expr, &tstate->final_meta.finalfn);
for (i = 1; i < num_args; i++) for (i = 1; i < num_args; i++)
{ {
tstate->final_meta->finalfn_fcinfo.arg[i] = (Datum) 0; tstate->final_meta.finalfn_fcinfo.arg[i] = (Datum) 0;
tstate->final_meta->finalfn_fcinfo.argnull[i] = true; tstate->final_meta.finalfn_fcinfo.argnull[i] = true;
} }
} }
} }
fcinfo->flinfo->fn_extra = (void *) tstate;
MemoryContextSwitchTo(oldcontext);
/* Need to init tstate->per_group_state->trans_value */
tstate->per_group_state->trans_value_isnull = true;
tstate->per_group_state->trans_value_initialized = false;
return tstate; return tstate;
} }
@ -366,6 +412,7 @@ group_state_advance(FAPerGroupState *per_group_state, FACombineFnMeta *combine_m
combine_meta->combfn_fcinfo.argnull[0] = per_group_state->trans_value_isnull; combine_meta->combfn_fcinfo.argnull[0] = per_group_state->trans_value_isnull;
combine_meta->combfn_fcinfo.arg[1] = newval; combine_meta->combfn_fcinfo.arg[1] = newval;
combine_meta->combfn_fcinfo.argnull[1] = newval_isnull; combine_meta->combfn_fcinfo.argnull[1] = newval_isnull;
combine_meta->combfn_fcinfo.isnull = false;
per_group_state->trans_value = FunctionCallInvoke(&combine_meta->combfn_fcinfo); per_group_state->trans_value = FunctionCallInvoke(&combine_meta->combfn_fcinfo);
per_group_state->trans_value_isnull = combine_meta->combfn_fcinfo.isnull; per_group_state->trans_value_isnull = combine_meta->combfn_fcinfo.isnull;
}; };
@ -389,48 +436,33 @@ group_state_advance(FAPerGroupState *per_group_state, FACombineFnMeta *combine_m
Datum Datum
tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS) tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
{ {
Oid inner_agg_input_collid;
FATransitionState *tstate = PG_ARGISNULL(0) ? NULL : (FATransitionState *) PG_GETARG_POINTER(0); FATransitionState *tstate = PG_ARGISNULL(0) ? NULL : (FATransitionState *) PG_GETARG_POINTER(0);
bytea *inner_agg_serialized_state = PG_ARGISNULL(5) ? NULL : PG_GETARG_BYTEA_P(5); bytea *inner_agg_serialized_state = PG_ARGISNULL(5) ? NULL : PG_GETARG_BYTEA_P(5);
bool inner_agg_serialized_state_isnull = PG_ARGISNULL(5) ? true : false; bool inner_agg_serialized_state_isnull = PG_ARGISNULL(5) ? true : false;
Datum inner_agg_deserialized_state; Datum inner_agg_deserialized_state;
AggState *fa_aggstate;
MemoryContext fa_context, old_context; MemoryContext fa_context, old_context;
/*
* we check for AggState explicitly as well because AggCheckCallContext
* will return true for window function contexts
*/
if (!AggCheckCallContext(fcinfo, &fa_context) || !IsA(fcinfo->context, AggState)) if (!AggCheckCallContext(fcinfo, &fa_context) || !IsA(fcinfo->context, AggState))
{ {
/* cannot be called directly because of internal-type argument */ /* cannot be called directly because of internal-type argument */
elog(ERROR, "finalize_agg_sfunc called in non-aggregate context"); elog(ERROR, "finalize_agg_sfunc called in non-aggregate context");
} }
fa_aggstate = castNode(AggState, fcinfo->context);
if (PG_ARGISNULL(1)) if (PG_ARGISNULL(1))
elog(ERROR, "finalize_agg_sfunc called with NULL aggfn"); elog(ERROR, "finalize_agg_sfunc called with NULL aggfn");
old_context = MemoryContextSwitchTo(fa_context); old_context = MemoryContextSwitchTo(fa_context);
if (tstate == NULL) if (tstate == NULL)
{ {
char *inner_agg_input_coll_schema = PG_ARGISNULL(2) ? NULL : NameStr(*PG_GETARG_NAME(2)); FAPerQueryState *qstate = (FAPerQueryState *) fcinfo->flinfo->fn_extra;
char *inner_agg_input_coll_name = PG_ARGISNULL(3) ? NULL : NameStr(*PG_GETARG_NAME(3)); if (qstate == NULL)
ArrayType *input_types = PG_ARGISNULL(4) ? NULL : PG_GETARG_ARRAYTYPE_P(4); {
Oid inner_agg_fn_oid = aggfnoid_from_aggname(PG_GETARG_TEXT_PP(1)); qstate = fa_perquery_state_init(fcinfo);
Assert(fcinfo->flinfo->fn_extra != NULL);
inner_agg_input_collid = }
collation_oid_from_name(inner_agg_input_coll_schema, inner_agg_input_coll_name); tstate = fa_transition_state_init(&fa_context, qstate, (AggState *) fcinfo->context);
/* okay, now we can initialize our transition state */
tstate = fa_transition_state_init(&fa_context,
inner_agg_fn_oid,
inner_agg_input_collid,
fa_aggstate,
input_types);
/* initial trans_value = the partial state of the inner agg from first invocation */ /* initial trans_value = the partial state of the inner agg from first invocation */
tstate->per_group_state->trans_value = tstate->per_group_state->trans_value =
inner_agg_deserialize(tstate->combine_meta, inner_agg_deserialize(&tstate->per_query_state->combine_meta,
inner_agg_serialized_state, inner_agg_serialized_state,
inner_agg_serialized_state_isnull, inner_agg_serialized_state_isnull,
&tstate->per_group_state->trans_value_isnull); &tstate->per_group_state->trans_value_isnull);
@ -441,7 +473,7 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
{ {
bool deser_isnull; bool deser_isnull;
bool call_combine; bool call_combine;
inner_agg_deserialized_state = inner_agg_deserialize(tstate->combine_meta, inner_agg_deserialized_state = inner_agg_deserialize(&tstate->per_query_state->combine_meta,
inner_agg_serialized_state, inner_agg_serialized_state,
inner_agg_serialized_state_isnull, inner_agg_serialized_state_isnull,
&deser_isnull); &deser_isnull);
@ -452,7 +484,7 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
* need to try that again if so. * need to try that again if so.
*/ */
call_combine = true; call_combine = true;
if (tstate->combine_meta->combinefn.fn_strict) if (tstate->per_query_state->combine_meta.combinefn.fn_strict)
{ {
if (tstate->per_group_state->trans_value_initialized == false && deser_isnull == false) if (tstate->per_group_state->trans_value_initialized == false && deser_isnull == false)
{ {
@ -467,7 +499,7 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
} }
if (call_combine) if (call_combine)
group_state_advance(tstate->per_group_state, group_state_advance(tstate->per_group_state,
tstate->combine_meta, &tstate->per_query_state->combine_meta,
inner_agg_deserialized_state, inner_agg_deserialized_state,
deser_isnull); deser_isnull);
} }
@ -491,21 +523,24 @@ tsl_finalize_agg_ffunc(PG_FUNCTION_ARGS)
elog(ERROR, "finalize_agg_ffunc called in non-aggregate context"); elog(ERROR, "finalize_agg_ffunc called in non-aggregate context");
} }
old_context = MemoryContextSwitchTo(fa_context); old_context = MemoryContextSwitchTo(fa_context);
if (OidIsValid(tstate->final_meta->finalfnoid)) if (OidIsValid(tstate->per_query_state->final_meta.finalfnoid))
{ {
/* don't execute if strict and the trans value is NULL or there are extra args (all extra /* don't execute if strict and the trans value is NULL or there are extra args (all extra
* args are always NULL) */ * args are always NULL) */
if (!(tstate->final_meta->finalfn.fn_strict && if (!(tstate->per_query_state->final_meta.finalfn.fn_strict &&
tstate->per_group_state->trans_value_isnull) && tstate->per_group_state->trans_value_isnull) &&
!(tstate->final_meta->finalfn.fn_strict && !(tstate->per_query_state->final_meta.finalfn.fn_strict &&
tstate->final_meta->finalfn_fcinfo.nargs > 1)) tstate->per_query_state->final_meta.finalfn_fcinfo.nargs > 1))
{ {
tstate->final_meta->finalfn_fcinfo.arg[0] = tstate->per_group_state->trans_value; tstate->per_query_state->final_meta.finalfn_fcinfo.arg[0] =
tstate->final_meta->finalfn_fcinfo.argnull[0] = tstate->per_group_state->trans_value;
tstate->per_query_state->final_meta.finalfn_fcinfo.argnull[0] =
tstate->per_group_state->trans_value_isnull; tstate->per_group_state->trans_value_isnull;
tstate->per_query_state->final_meta.finalfn_fcinfo.isnull = false;
tstate->per_group_state->trans_value = tstate->per_group_state->trans_value =
FunctionCallInvoke(&tstate->final_meta->finalfn_fcinfo); FunctionCallInvoke(&tstate->per_query_state->final_meta.finalfn_fcinfo);
tstate->per_group_state->trans_value_isnull = tstate->final_meta->finalfn_fcinfo.isnull; tstate->per_group_state->trans_value_isnull =
tstate->per_query_state->final_meta.finalfn_fcinfo.isnull;
} }
} }
MemoryContextSwitchTo(old_context); MemoryContextSwitchTo(old_context);