Thread: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Thomas Munro
Date:
Hi hackers,

Andrew Gierth complained off-list that TupleDescCopy() doesn't clear
atthasdef.  Yeah, that's an oversight.  The function is new in commit
cc5f81366c36 and was written by me to support "flat" (pointer-free)
tuple descriptors for use in DSM.  Following the example of
CreateTupleDescCopy() I think it should also clear attnotnull and
attidentity.  Please see attached.

-- 
Thomas Munro
http://www.enterprisedb.com

Attachment

Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Vik Fearing
Date:
On 11/29/2017 08:58 AM, Thomas Munro wrote:
> Hi hackers,
> 
> Andrew Gierth complained off-list that TupleDescCopy() doesn't clear
> atthasdef.  Yeah, that's an oversight.  The function is new in commit
> cc5f81366c36 and was written by me to support "flat" (pointer-free)
> tuple descriptors for use in DSM.  Following the example of
> CreateTupleDescCopy() I think it should also clear attnotnull and
> attidentity.  Please see attached.

This trivial patch is a clear oversight in the original patch.  Thank
you to Andrew for some explanation off-list to help me understand tuple
descriptors better.

Ready for committer.
-- 
Vik Fearing                                          +33 6 46 75 15 36
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support


Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Tom Lane
Date:
Thomas Munro <thomas.munro@enterprisedb.com> writes:
> Andrew Gierth complained off-list that TupleDescCopy() doesn't clear
> atthasdef.  Yeah, that's an oversight.  The function is new in commit
> cc5f81366c36 and was written by me to support "flat" (pointer-free)
> tuple descriptors for use in DSM.  Following the example of
> CreateTupleDescCopy() I think it should also clear attnotnull and
> attidentity.  Please see attached.

I've pushed this with some editorialization.  I added some comments, and
noting that you have TupleDescCopy() copying the entire attribute array
in one memcpy, I propagated that same approach into CreateTupleDescCopy
and CreateTupleDescCopyConstr.  This should make things a bit faster
since memcpy can spend more time in its maximum-stride loop.

The reason I note this explicitly is that I don't find it to be
entirely safe.  If ATTRIBUTE_FIXED_PART_SIZE were less than
sizeof(FormData_pg_attribute) due to alignment padding at the end of
the struct, I think we would get some valgrind complaints about copying
uninitialized data, since there are code paths in which only the first
ATTRIBUTE_FIXED_PART_SIZE bytes of each array entry get filled in.
Now, currently I don't think there's any padding there anyway on any
platform we support.  But if we're going to do things like this, I think
that we ought to explicitly make ATTRIBUTE_FIXED_PART_SIZE the same as
sizeof(FormData_pg_attribute).  Hence, I propose the attached follow-on
patch.

            regards, tom lane

diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index f1f4423..67d3d3d 100644
*** a/src/backend/access/common/tupdesc.c
--- b/src/backend/access/common/tupdesc.c
*************** CreateTemplateTupleDesc(int natts, bool
*** 57,65 ****
       * since we declare the array elements as FormData_pg_attribute for
       * notational convenience.  However, we only guarantee that the first
       * ATTRIBUTE_FIXED_PART_SIZE bytes of each entry are valid; most code that
!      * copies tupdesc entries around copies just that much.  In principle that
!      * could be less due to trailing padding, although with the current
!      * definition of pg_attribute there probably isn't any padding.
       */
      desc = (TupleDesc) palloc(offsetof(struct tupleDesc, attrs) +
                                natts * sizeof(FormData_pg_attribute));
--- 57,69 ----
       * since we declare the array elements as FormData_pg_attribute for
       * notational convenience.  However, we only guarantee that the first
       * ATTRIBUTE_FIXED_PART_SIZE bytes of each entry are valid; most code that
!      * copies tupdesc entries around copies just that much.  Currently, that
!      * symbol is just defined as sizeof(FormData_pg_attribute) anyway, so this
!      * distinction is somewhat academic.  If there were any trailing padding
!      * space in FormData_pg_attribute, it'd be possible to define
!      * ATTRIBUTE_FIXED_PART_SIZE to omit it, but that might cause complaints
!      * from valgrind about copying uninitialized padding bytes, so it seems
!      * best to keep them the same.
       */
      desc = (TupleDesc) palloc(offsetof(struct tupleDesc, attrs) +
                                natts * sizeof(FormData_pg_attribute));
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 6104254..739e784 100644
*** a/src/include/catalog/pg_attribute.h
--- b/src/include/catalog/pg_attribute.h
*************** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
*** 175,183 ****
   * guaranteed-not-null part of a pg_attribute row.  This is in fact as much
   * of the row as gets copied into tuple descriptors, so don't expect you
   * can access fields beyond attcollation except in a real tuple!
   */
  #define ATTRIBUTE_FIXED_PART_SIZE \
!     (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))

  /* ----------------
   *        Form_pg_attribute corresponds to a pointer to a tuple with
--- 175,188 ----
   * guaranteed-not-null part of a pg_attribute row.  This is in fact as much
   * of the row as gets copied into tuple descriptors, so don't expect you
   * can access fields beyond attcollation except in a real tuple!
+  *
+  * Since the introduction of CATALOG_VARLEN, this can just be defined as
+  * sizeof(FormData_pg_attribute).  It's possible that that would include
+  * some trailing alignment padding, but that's harmless (and anyway there
+  * is no such padding given the current contents of pg_attribute).
   */
  #define ATTRIBUTE_FIXED_PART_SIZE \
!     sizeof(FormData_pg_attribute)

  /* ----------------
   *        Form_pg_attribute corresponds to a pointer to a tuple with

Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Thomas Munro
Date:
On Thu, Jan 4, 2018 at 12:01 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Thomas Munro <thomas.munro@enterprisedb.com> writes:
>> Andrew Gierth complained off-list that TupleDescCopy() doesn't clear
>> atthasdef.  Yeah, that's an oversight.  The function is new in commit
>> cc5f81366c36 and was written by me to support "flat" (pointer-free)
>> tuple descriptors for use in DSM.  Following the example of
>> CreateTupleDescCopy() I think it should also clear attnotnull and
>> attidentity.  Please see attached.
>
> I've pushed this with some editorialization.  I added some comments, and
> noting that you have TupleDescCopy() copying the entire attribute array
> in one memcpy, I propagated that same approach into CreateTupleDescCopy
> and CreateTupleDescCopyConstr.  This should make things a bit faster
> since memcpy can spend more time in its maximum-stride loop.

Thanks.

> The reason I note this explicitly is that I don't find it to be
> entirely safe.  If ATTRIBUTE_FIXED_PART_SIZE were less than
> sizeof(FormData_pg_attribute) due to alignment padding at the end of
> the struct, I think we would get some valgrind complaints about copying
> uninitialized data, since there are code paths in which only the first
> ATTRIBUTE_FIXED_PART_SIZE bytes of each array entry get filled in.
> Now, currently I don't think there's any padding there anyway on any
> platform we support.  But if we're going to do things like this, I think
> that we ought to explicitly make ATTRIBUTE_FIXED_PART_SIZE the same as
> sizeof(FormData_pg_attribute).  Hence, I propose the attached follow-on
> patch.

That seems correct.

If there is any system where sizeof(FormData_pg_attribute) !=
(offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid)), won't
load_relcache_init_file() get upset?  Oh, I see it would just go to
read_failed and then "do it the hard way".

+1

-- 
Thomas Munro
http://www.enterprisedb.com


Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Thomas Munro
Date:
On Thu, Jan 4, 2018 at 12:29 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:
> If there is any system where sizeof(FormData_pg_attribute) !=
> (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid)), won't
> load_relcache_init_file() get upset?  Oh, I see it would just go to
> read_failed and then "do it the hard way".

This doesn't make any sense, we're free to change that file format in
a major release anyway.  I withdraw that comment!

-- 
Thomas Munro
http://www.enterprisedb.com


Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Tom Lane
Date:
Thomas Munro <thomas.munro@enterprisedb.com> writes:
> If there is any system where sizeof(FormData_pg_attribute) !=
> (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid)), won't
> load_relcache_init_file() get upset?  Oh, I see it would just go to
> read_failed and then "do it the hard way".

Right, so we needn't include a catversion bump (or more likely,
a RELCACHE_INIT_FILEMAGIC change) in that patch.

            regards, tom lane


Re: TupleDescCopy doesn't clear atthasdef, attnotnull, attidentity

From
Tom Lane
Date:
I wrote:
> The reason I note this explicitly is that I don't find it to be
> entirely safe.  If ATTRIBUTE_FIXED_PART_SIZE were less than
> sizeof(FormData_pg_attribute) due to alignment padding at the end of
> the struct, I think we would get some valgrind complaints about copying
> uninitialized data, since there are code paths in which only the first
> ATTRIBUTE_FIXED_PART_SIZE bytes of each array entry get filled in.

After further study I think this is a false alarm.  valgrind doesn't
actually whine when you memcpy some undefined bytes: it just propagates
the undefined-ness of those bytes to the destination, and complains only
if the program uses the destination bytes to do something meaningful.
(Were this not so, copying structs containing embedded padding bytes
would always cause bogus warnings.)

There might still be value in redefining ATTRIBUTE_FIXED_PART_SIZE as
sizeof(FormData_pg_attribute), but I'm not sure, and am sort of inclined
to leave it alone.

            regards, tom lane