Hi,
On 2023-02-09 11:38:18 +0100, Drouvot, Bertrand wrote:
> Please find attached a patch proposal for $SUBJECT.
>
> The idea has been raised in [1] by Andres: it would allow to simplify even
> more the work done to
> generate pg_stat_get_xact*() functions with Macros.
Thanks!
I think this is useful beyond being able to generate those functions with
macros. The fact that we had to deal with transactional code in pgstatfuncs.c
meant that a lot of the relevant itnernals had to be exposed "outside" pgstat,
which seems architecturally not great.
> Indeed, with the reconciliation done in find_tabstat_entry() then all the
> pg_stat_get_xact*() functions
> (making use of find_tabstat_entry()) now "look the same" (should they take
> into account live subtransactions or not).
I'm not bothered by making all of pg_stat_get_xact* functions more expensive,
they're not a hot code path. But if we need to, we could just add a parameter
to find_tabstat_entry() indicating whether we need to reconcile or not.
> /* save stats for this function, later used to compensate for recursion
> */
> - fcu->save_f_total_time = pending->f_counts.f_total_time;
> + fcu->save_f_total_time = pending->f_total_time;
>
> /* save current backend-wide total time */
> fcu->save_total = total_func_time;
The diff is noisy due to all the mechanical changes like the above. Could that
be split into a separate commit?
> find_tabstat_entry(Oid rel_id)
> {
> PgStat_EntryRef *entry_ref;
> + PgStat_TableStatus *tablestatus = NULL;
>
> entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION,
> MyDatabaseId, rel_id);
> if (!entry_ref)
> entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION,
> InvalidOid, rel_id);
>
> if (entry_ref)
> - return entry_ref->pending;
> - return NULL;
> + {
> + PgStat_TableStatus *tabentry = (PgStat_TableStatus *)
> entry_ref->pending;
I'd add an early return for the !entry_ref case, that way you don't need to
indent the bulk of the function.
> + PgStat_TableXactStatus *trans;
> +
> + tablestatus = palloc(sizeof(PgStat_TableStatus));
> + memcpy(tablestatus, tabentry, sizeof(PgStat_TableStatus));
For things like this I'd use
*tablestatus = *tabentry;
that way the compiler will warn you about mismatching types, and you don't
need the sizeof().
> + /* live subtransactions' counts aren't in t_counts yet */
> + for (trans = tabentry->trans; trans != NULL; trans =
> trans->upper)
> + {
> + tablestatus->t_counts.t_tuples_inserted +=
> trans->tuples_inserted;
> + tablestatus->t_counts.t_tuples_updated +=
> trans->tuples_updated;
> + tablestatus->t_counts.t_tuples_deleted +=
> trans->tuples_deleted;
> + }
> + }
Hm, why do we end uup with t_counts still being used here, but removed other
places?
> diff --git a/src/backend/utils/adt/pgstatfuncs.c
> b/src/backend/utils/adt/pgstatfuncs.c
> index 6737493402..40a6fbf871 100644
> --- a/src/backend/utils/adt/pgstatfuncs.c
> +++ b/src/backend/utils/adt/pgstatfuncs.c
> @@ -1366,7 +1366,10 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
> if ((tabentry = find_tabstat_entry(relid)) == NULL)
> result = 0;
> else
> + {
> result = (int64) (tabentry->t_counts.t_numscans);
> + pfree(tabentry);
> + }
>
> PG_RETURN_INT64(result);
> }
I don't think we need to bother with individual pfrees in this path. The
caller will call the function in a dedicated memory context, that'll be reset
very soon after this.
Greetings,
Andres Freund