Thread

  1. Re: [PATCH] Allow complex data for GUC extra.

    Robert Haas <robertmhaas@gmail.com> — 2025-12-08T16:48:35Z

    On Sat, Dec 6, 2025 at 2:08 AM Bryan Green <dbryan.green@gmail.com> wrote:
    > > I think I'm missing something obvious here. call_string_check_hook
    > > doesn't do any memory context management - it just calls the hook.
    
    No, it does do memory management. It has a PG_TRY()/PG_CATCH() block
    to ensure that we don't forget to GUC_free(*newval) in case of error.
    I was trying to figure out where we were doing the relevant memory
    management today, and then extend that to handle the new thing. But I
    am guilty of fuzzy thinking here, because we're talking about where
    the "extra" memory is managed, not the memory for "newval". So the
    logic we care about is in set_config_with_handle() just as you said:
    
                    /* Perhaps we didn't install newextra anywhere */
                    if (newextra && !extra_field_used(record, newextra))
                        guc_free(newextra);
    
    What I hadn't quite internalized previously was that there's no
    PG_TRY/PG_CATCH block here right now because we assume that (1) we
    assume the check hook won't allocate the extra value until it's ready
    to return, so it will never leak a value by allocating it and then
    erroring out and (2) we take care to ensure that no errors can happen
    in the GUC code itself after the extra value has been returned and
    before we either free it or save a pointer to it someplace.
    
    But having said that, I'm inclined to think that handling the memory
    management concerns inside call_WHATEVER_check_hook() still makes some
    sense. It seems to me that if we do that, set_config_with_handle()
    needs very little change. All it needs to do differently is: wherever
    it would guc_free(newextra), it can call some new helper function that
    will either just guc_free() or alternatively
    MemoryContextDelete(GetMemoryChunkContext()) depending on flags. I
    think this is good, because set_config_with_handle() is already pretty
    complicated, and I'd rather not inject more complexity into that
    function.
    
    For this to work, each call_WHATEVER_check_hook() function would need
    a PG_TRY()/PG_CATCH() block, rather than only call_string_check_hook()
    as currently. Or alternatively, and I think this might be an appealing
    option, we could say that this feature is only available for string
    values, and the other call_WHATEVER_check_hook() functions just assert
    that the GUC_EXTRA_IS_CONTEXT flag is not set. I don't see why you'd
    need a complex "extra" value for a bool or int or enum or real-valued
    GUC -- how much complex parsing can you need to do on a non-string
    value?
    
    I think the call_string_check_hook logic in the v2 patch is
    approximately correct. This can be tightened up:
    
            if (result)
            {
                if (*extra != NULL)
                    MemoryContextSetParent(extra_cxt, GUCMemoryContext);
                else
                    MemoryContextDelete(extra_cxt);
            }
            else
            {
                MemoryContextDelete(extra_cxt);
            }
    
    You can instead write:
    
            if (result  != NULL && *extra != NULL)
                MemoryContextSetParent(extra_cxt, GUCMemoryContext);
            else
                MemoryContextDelete(extra_cxt);
    
    > One additional fix: if a check hook succeeds but returns NULL for extra,
    > we delete the empty context rather than reparenting it to avoid leaking
    > contexts that would never be cleaned up.
    
    Yeah, avoiding leaking contexts seems like one of the key challenges
    here. I'm not sure whether we would ever have a check hook that either
    returns a null or non-null *extra depending on the situation, but it
    seems good to be prepared for that case. I notice that guc_free()
    silently accepts a null pointer, so presumably a similar case with a
    "flat" GUC extra could exist and work today.
    
    Also, to respond to your later emails, I agree that the new context
    shouldn't be created under GUCMemoryContext. As discussed with Tom
    earlier, we don't want it to be a long-lived context. I think
    CurrentMemoryContext is OK provided that CurrentMemoryContext is
    always a child of TopTransactionContext, because even if we leak
    something, it will only survive until the end of the transaction at
    latest. However, if CurrentMemoryContext can be something like
    TopMemoryContext or CacheMemoryContext, then we might want to think a
    little harder. I'm not sure whether that's possible -- perhaps you
    would like to investigate? Think particularly about GUCs set during
    server startup -- maybe in the postmaster, maybe in a backend very
    early during initialization. Also maybe configuration reloads while
    the backend is idle.
    
    I think we ought to make this patch use MemoryContextSetIdentifier()
    to make any leaks easier to debug. If a memory context dump shows that
    you've got a whole bunch of contexts floating around, or one really
    big one, and they're all just named "GUC extra context" or whatever,
    that's going to be pretty unhelpful. If the patch does
    MemoryContextSetIdentifier(extra_cxt, conf->name), you'll be able to
    see which GUC is responsible.
    
    I think you should port a couple of the core GUCs use this new
    mechanism. I suggest specifically check_synchronous_standby_names and
    check_synchronized_standby_slots. That should give us some better
    insight into how well this mechanism really works and whether it is
    more convenient in practice than what we're making check hooks do
    today. I thought about proposing that if you do that, you might be
    able to just drop this test module, but both of those GUCs are
    PGC_SIGHUP, so they wouldn't be good for testing the behavior with
    SET, SET LOCAL, RESET, etc. So we might need to either find a case
    that can benefit from this mechanism that is PGC_USERSET or PGC_SUSET,
    or keep the test module in some form. backtrace_functions is a
    possibility, but it's not altogether clear that a non-flat
    representation is better in that case, and it doesn't seem great in
    terms of being able to write simple tests, either.
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com