Thread

Commits

  1. Always use ReleaseTupleDesc after lookup_rowtype_tupdesc et al.

  1. Life cycles of tuple descriptors

    Chapman Flack <chap@anastigmatix.net> — 2021-12-14T20:42:12Z

    Hi,
    
    There are some things about the life cycle of the common TupleDesc
    that I'm not 100% sure about.
    
    1. In general, if you get one from relcache or typcache, it is
       reference-counted, right?
    
    2. The only exception I know of is if you ask the typcache for
       a blessed one (RECORD+typmod), and it is found in the shared-memory
       cache. It is not refcounted then. If it is found only locally,
       it is refcounted.
    
    3. Is that shared case the only way you could see a non-refcounted
       TupleDesc handed to you by the typcache?
    
    4. How long can such a non-refcounted TupleDesc from the typcache
       be counted on to live?
    
       There is a comment in expandedrecord.c: "It it's not refcounted, just
       assume it will outlive the expanded object."
    
       That sounds like confidence, but is it confidence in the longevity
       of shared TupleDescs, or in the fleeting lives of expanded records?
    
       The same comment says "(This can happen for shared record types,
       for instance.)" Does the "for instance" mean there are now, or just
       in future might be, other cases where the typcache hands you
       a non-refcounted TupleDesc?
    
    5. When a constructed TupleDesc is blessed, the copy placed in the cache
       by assign_record_type_typmod is born with a refcount of 1. Assuming every
       later user of that TupleDesc plays nicely with balanced pin and release,
       what event(s), if any, could ever occur to decrease that initial 1 to 0?
    
    Thanks for any help getting these points straight!
    
    Regards,
    -Chap
    
    
    
    
  2. Re: Life cycles of tuple descriptors

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-12-14T23:03:00Z

    Chapman Flack <chap@anastigmatix.net> writes:
    > There are some things about the life cycle of the common TupleDesc
    > that I'm not 100% sure about.
    
    > 1. In general, if you get one from relcache or typcache, it is
    >    reference-counted, right?
    
    Tupdescs for named composite types should be, since those are
    potentially modifiable by DDL.  The refcount allows a stale tupdesc
    to go away when it's no longer referenced anywhere.
    
    Tupdescs for RECORD types are a different story: there's no way to
    change them once created, so they'll live as long as the process
    does (at least for tupdescs kept in the typcache).  Refcounting
    isn't terribly necessary in that case; and at least for the shared
    tupdesc case, we don't do it, to avoid questions of modifying a
    piece of shared state.
    
    > 3. Is that shared case the only way you could see a non-refcounted
    >    TupleDesc handed to you by the typcache?
    
    I'm not sure what happens for a non-shared RECORD tupdesc, but it
    probably wouldn't be wise to assume anything either way.
    
    > 5. When a constructed TupleDesc is blessed, the copy placed in the cache
    >    by assign_record_type_typmod is born with a refcount of 1. Assuming every
    >    later user of that TupleDesc plays nicely with balanced pin and release,
    >    what event(s), if any, could ever occur to decrease that initial 1 to 0?
    
    That refcount is describing the cache's own reference, so as long as
    that reference remains it'd be incorrect to decrease the refcount to 0.
    
    			regards, tom lane
    
    
    
    
  3. Re: Life cycles of tuple descriptors

    Chapman Flack <chap@anastigmatix.net> — 2021-12-14T23:35:48Z

    On 12/14/21 18:03, Tom Lane wrote:
    
    > Tupdescs for RECORD types are a different story: ... Refcounting
    > isn't terribly necessary in that case; and at least for the shared
    > tupdesc case, we don't do it, to avoid questions of modifying a
    > piece of shared state.
    
    Ok, that's kind of what I was getting at here:
    
    >> 5. When a constructed TupleDesc is blessed, the copy placed in the cache
    >>    by assign_record_type_typmod is born with a refcount of 1. ...
    >>    what event(s), if any, could ever occur to decrease that initial 1 ...
    >
    > That refcount is describing the cache's own reference, so as long as
    > that reference remains it'd be incorrect to decrease the refcount to 0.
    
    In the case of a blessed RECORD+typmod tupdesc, *is* there any event that
    could ever cause the cache to drop that reference? Or is the tupdesc just
    going to live there for the life of the backend, its refcount sometimes
    going above and back down to but never below 1?
    
    That would fit with your "refcounting isn't terribly necessary in that
    case". If that's how it works, it's interesting having the two different
    patterns: if it's a shared one, it has refcount -1 and you never fuss
    with it and it never goes away; if it's a local one it has a non-negative
    refcount and you go through all the motions and it never goes away anyway.
    
    >> 3. Is that shared case the only way you could see a non-refcounted
    >>    TupleDesc handed to you by the typcache?
    > 
    > I'm not sure what happens for a non-shared RECORD tupdesc, but it
    > probably wouldn't be wise to assume anything either way.
    
    If I'm reading this right, for the non-shared case, the copy that goes
    into the cache is made refcounted. (The copy you presented for blessing
    gets the assigned typmod written in it, and no change to its refcount
    field.)
    
    There's really just one thing I'm interested in assuming:
    
    *In general*, if I encounter a tupdesc with -1 refcount, I had better not
    assume much about its longevity. It might be in a context that's about to
    go away. If I'll be wanting it later, I had better defensively copy it
    into some context I've chosen.
    
    (Ok, I guess if it's a tupdesc provided to me in a function call, I can
    assume it is good for the duration of the call, or if it's part of an
    SPI result, I can assume it's good until SPI_finish.)
    
    But if I have gone straight to the typcache to ask for a RECORD tupdesc,
    and the one it gives me has -1 refcount, is it reasonable to assume
    I can retain a reference to that without the defensive copy?
    
    Regards,
    -Chap
    
    
    
    
  4. Re: Life cycles of tuple descriptors

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-12-15T01:02:28Z

    Chapman Flack <chap@anastigmatix.net> writes:
    > But if I have gone straight to the typcache to ask for a RECORD tupdesc,
    > and the one it gives me has -1 refcount, is it reasonable to assume
    > I can retain a reference to that without the defensive copy?
    
    The API contract for lookup_rowtype_tupdesc specifies that you must "call
    ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc".
    It's safe to assume that the tupdesc will stick around as long as you
    haven't done that.
    
    APIs that don't mention a refcount are handing you a tupdesc of uncertain
    lifespan (no more than the current query, likely), so if you want the
    tupdesc to last a long time you'd better copy it into storage you control.
    
    			regards, tom lane
    
    
    
    
  5. Re: Life cycles of tuple descriptors

    Chapman Flack <chap@anastigmatix.net> — 2021-12-15T01:58:32Z

    On 12/14/21 20:02, Tom Lane wrote:
    > The API contract for lookup_rowtype_tupdesc specifies that you must "call
    > ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc".
    > It's safe to assume that the tupdesc will stick around as long as you
    > haven't done that.
    
    I think what threw me was having a function whose API contract mentions
    reference counts, but that sometimes gives me things that don't have them.
    
    But I guess, making the between-the-lines of the contract explicit,
    if lookup_rowtype_tupdesc is contracted to give me a tupdesc that sticks
    around for as long as I haven't called ReleaseTupleDesc, and it sometimes
    elects to give me one for which ReleaseTupleDesc is a no-op, the contract
    is still that the thing sticks around for (at least) as long as I haven't
    done that.
    
    Cool. :)
    
    Oh, hmm, maybe one thing in that API comment ought to be changed. It says
    I must call ReleaseTupleDesc *or* DecrTupleDescRefCount. Maybe that dates
    from before the shared registry? ReleaseTupleDesc is safe, but anybody who
    uses DecrTupleDescRefCount on a lookup_rowtype_tupdesc result could be
    in for an assertion failure if a non-refcounted tupdesc is returned.
    
    Regards,
    -Chap
    
    
    
    
  6. Re: Life cycles of tuple descriptors

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-12-15T02:14:38Z

    Chapman Flack <chap@anastigmatix.net> writes:
    > On 12/14/21 20:02, Tom Lane wrote:
    >> The API contract for lookup_rowtype_tupdesc specifies that you must "call
    >> ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc".
    >> It's safe to assume that the tupdesc will stick around as long as you
    >> haven't done that.
    
    > I think what threw me was having a function whose API contract mentions
    > reference counts, but that sometimes gives me things that don't have them.
    
    That's supposed to be hidden under ReleaseTupleDesc; you shouldn't have to
    think about it.
    
    > Oh, hmm, maybe one thing in that API comment ought to be changed. It says
    > I must call ReleaseTupleDesc *or* DecrTupleDescRefCount. Maybe that dates
    > from before the shared registry? ReleaseTupleDesc is safe, but anybody who
    > uses DecrTupleDescRefCount on a lookup_rowtype_tupdesc result could be
    > in for an assertion failure if a non-refcounted tupdesc is returned.
    
    Yeah, I was just wondering the same.  I think DecrTupleDescRefCount
    is safe if you know you are looking up a named composite type, but
    maybe that's still too much familiarity with typcache innards.
    
    			regards, tom lane
    
    
    
    
  7. Re: Life cycles of tuple descriptors

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-12-15T22:50:41Z

    I wrote:
    > Chapman Flack <chap@anastigmatix.net> writes:
    >> Oh, hmm, maybe one thing in that API comment ought to be changed. It says
    >> I must call ReleaseTupleDesc *or* DecrTupleDescRefCount. Maybe that dates
    >> from before the shared registry? ReleaseTupleDesc is safe, but anybody who
    >> uses DecrTupleDescRefCount on a lookup_rowtype_tupdesc result could be
    >> in for an assertion failure if a non-refcounted tupdesc is returned.
    
    > Yeah, I was just wondering the same.  I think DecrTupleDescRefCount
    > is safe if you know you are looking up a named composite type, but
    > maybe that's still too much familiarity with typcache innards.
    
    Here's a draft patch for this.  There are several places that are
    directly using DecrTupleDescRefCount after lookup_rowtype_tupdesc
    or equivalent, which'd now be forbidden.  I think they are all safe
    given the assumption that the typcache's tupdescs for named composites
    are refcounted.  (The calls in expandedrecord.c could be working
    with RECORD, but those code paths just checked that the tupdesc
    is refcounted.)  So there's no actual bug here, and no reason to
    back-patch, but this seems like a good idea to decouple callers
    a bit more from typcache's internal logic.  None of these call
    sites are so performance-critical that one extra test will hurt.
    
    			regards, tom lane
    
    
  8. Re: Life cycles of tuple descriptors

    Thomas Munro <thomas.munro@gmail.com> — 2021-12-15T23:18:04Z

    On Thu, Dec 16, 2021 at 11:51 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Here's a draft patch for this.  There are several places that are
    > directly using DecrTupleDescRefCount after lookup_rowtype_tupdesc
    > or equivalent, which'd now be forbidden.  I think they are all safe
    > given the assumption that the typcache's tupdescs for named composites
    > are refcounted.  (The calls in expandedrecord.c could be working
    > with RECORD, but those code paths just checked that the tupdesc
    > is refcounted.)  So there's no actual bug here, and no reason to
    > back-patch, but this seems like a good idea to decouple callers
    > a bit more from typcache's internal logic.  None of these call
    > sites are so performance-critical that one extra test will hurt.
    
    LGTM.
    
    
    
    
  9. Re: Life cycles of tuple descriptors

    Chapman Flack <chap@anastigmatix.net> — 2021-12-15T23:20:11Z

    On 12/15/21 17:50, Tom Lane wrote:
    
    > Here's a draft patch for this.  There are several places that are
    > directly using DecrTupleDescRefCount after lookup_rowtype_tupdesc
    > or equivalent, which'd now be forbidden.  I think they are all safe
    > given the assumption that the typcache's tupdescs for named composites
    > are refcounted.  (The calls in expandedrecord.c could be working
    > with RECORD, but those code paths just checked that the tupdesc
    > is refcounted.)  So there's no actual bug here, and no reason to
    > back-patch, but this seems like a good idea to decouple callers
    > a bit more from typcache's internal logic.
    
    I agree with the analysis at each of those sites, and the new comment
    clears up everything that had puzzled me before.
    
    Regards,
    -Chap
    
    
    
    
  10. Re: Life cycles of tuple descriptors

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-12-15T23:58:59Z

    Thomas Munro <thomas.munro@gmail.com> writes:
    > On Thu, Dec 16, 2021 at 11:51 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> Here's a draft patch for this.
    
    > LGTM.
    
    Pushed, thanks for looking.
    
    			regards, tom lane