Hi, On 2023-05-23 18:23:00 +0200, Tomas Vondra wrote: > While looking for other places allocating stuff in ExecutorState (for > the UPDATE case) and leaving it there, I found two more cases:
For a bit I thought there was a similar problem in ExecWithCheckOptions() - but we error out immediately afterwards, so there's no danger of accumulating memory. I find it quite depressing that we have at least four copies of: /* * If the tuple has been routed, it's been converted to the partition's * rowtype, which might differ from the root table's. We must convert it * back to the root table's rowtype so that val_desc in the error message * matches the input tuple. */ if (resultRelInfo->ri_RootResultRelInfo) { ResultRelInfo *rootrel = resultRelInfo->ri_RootResultRelInfo; TupleDesc old_tupdesc; AttrMap *map; root_relid = RelationGetRelid(rootrel->ri_RelationDesc); tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a * new one. */ if (map != NULL) slot = execute_attr_map_slot(map, slot, MakeTupleTableSlot(tupdesc, &TTSOpsVirtual)); modifiedCols = bms_union(ExecGetInsertedCols(rootrel, estate), ExecGetUpdatedCols(rootrel, estate)); } Once in ExecPartitionCheckEmitError(), *twice* in ExecConstraints(), ExecWithCheckOptions(). Some of the partitioning stuff has been added in a really myopic way. Greetings, Andres Freund