Decoupling our alignment assumptions about int64 and double

  • Jump to comment-1
    Tom Lane<tgl@sss.pgh.pa.us>
    Jan 29, 2026, 1:20 AM UTC
    Over in the long-running thread about resurrecting AIX support,
    one salient concern is that AIX has alignof(int64) = 8 but
    alignof(double) = 4. This breaks all our code that supposes
    that ALIGNOF_DOUBLE is the platform's maximum alignment spec.
    We had coped with this via various unspeakable klugery back
    when AIX was considered supported, but nobody wants to put
    back those restrictions if we re-support AIX. (See my gripe
    at [1], but I believe Heikki and Andres complained of this
    in more detail long ago.)
    Here is a modest proposal for fixing that in a clean way.
    Let's break TYPALIGNDOUBLE into three values, TYPALIGNDOUBLE
    for float8 only, TYPALIGN_INT64 for 64-bit integral types
    including pointers, and TYPALIGN_MAX to represent MAXALIGN
    alignment regardless of which of the first two determine it.
    We need TYPALIGN_MAX because that's the value composite
    types should use, so that their alignment doesn't change
    if somebody adds or deletes a column of a relevant type.
    Given that design, the patch is pretty straightforward,
    and smaller than I feared it might be. (Though I might
    have missed a spot or two.)
    I think this is good cleanup and deserves consideration
    whether we accept the AIX patch soon or not.
    A loose end that deserves mention is that I noticed we
    are very inconsistent about the length/alignment
    attributed to polymorphic types:
    select typname, typlen, typalign from pg_type where typname like 'any%';
    typnametyplentypalign
    any4i
    anyarray-1m
    anycompatible4i
    anycompatiblearray-1m
    anycompatiblemultirange-1m
    anycompatiblenonarray4i
    anycompatiblerange-1m
    anyelement4i
    anyenum4i
    anymultirange-1m
    anynonarray4i
    anyrange-1m
    (12 rows)
    (Previous to this patch, the 'm' entries were 'd'.)
    In one sense this doesn't really matter, since no actual
    value should have any of these types attributed to it;
    but it annoys me that they're not all alike. I'm tempted to
    make them all 4/'i' which seems to be the older convention.
    A more aggressive answer would be to change these to some
    actually-illegal values, in hopes of catching anyplace where
    we did try to rely on the values. But that seems like
    material for a different patch.
    		regards, tom lane
    [1] https://www.postgresql.org/message-id/581905.1769550155%40sss.pgh.pa.us
    • Jump to comment-1
      Andres Freund<andres@anarazel.de>
      Jan 29, 2026, 4:13 PM UTC
      Hi,
      Thanks for the patch. Looks like a decent improvement.
      On 2026-01-28 20:20:24 -0500, Tom Lane wrote:
      One of the concerns that prevented this from being done long
      ago was not wanting to add overhead to tuple forming/deforming.
      However that concern seems gone now, because we map TYPALIGN_xxx
      values to numeric alignments in populatecompactattribute()
      which is not so performance-critical. It might be worth
      worrying about the increased cost of attalignnominal(),
      but that macro is not that heavily used IMO.
      Yea, it shouldn't matter too much these days. We might want to verify that
      the array code isn't overly affected, e.g. arrayiternext() was deemed perf
      critical enough by someone to make it an inline function. I don't know if the
      compiler is somehow smart enough to move the conditionals outside of a loop
      over arrayiternext().
      Perhaps we should make attalignnominal() first determine the numerical
      alignment value and then have it use TYPEALIGN()? I think that'd be more
      likely to be pulled out of loops by the compile.
      Perhaps it's time to reformat attalignnominal() into an static inline? It's
      pretty hard to read.
      I don't love the 'l' for TYPALIGN_INT64, but I guess I don't really have a
      better suggestion.
      It wouldn't hurt to have a short SQL level test for creating a type with int8
      & max alignments.
      Greetings,
      Andres Freund
      • Jump to comment-1
        Tom Lane<tgl@sss.pgh.pa.us>
        Jan 29, 2026, 4:33 PM UTC
        Andres Freund <andres@anarazel.de> writes:
        Thanks for the patch. Looks like a decent improvement.
        Thanks for looking!
        On 2026-01-28 20:20:24 -0500, Tom Lane wrote:
        ... It might be worth
        worrying about the increased cost of attalignnominal(),
        but that macro is not that heavily used IMO.
        Perhaps we should make attalignnominal() first determine the numerical
        alignment value and then have it use TYPEALIGN()? I think that'd be more
        likely to be pulled out of loops by the compile.
        Yeah, I was thinking about actually breaking that into two source-code
        steps, a function to map TYPALIGN_xxx to numeric alignment and then a
        replacement for attalignnominal that takes a numeric alignment.
        If you think it's worth worrying about I'm happy to do that.
        Perhaps it's time to reformat attalignnominal() into an static inline? It's
        pretty hard to read.
        +1, I was not revisiting any of that for this draft, but if we're going
        to refactor it then an inline function seems good.
        I don't love the 'l' for TYPALIGN_INT64, but I guess I don't really have a
        better suggestion.
        Of course I was thinking 'l' for "long", but I agree it's not great
        typographically. One idea is 'L' not 'l', but that gives up
        consistency for visual separation. Any other ideas out there?
        It wouldn't hurt to have a short SQL level test for creating a type with int8
        & max alignments.
        hmm ... yeah, I guess those code paths might not be covered already.
        		regards, tom lane
        • Jump to comment-1
          Tom Lane<tgl@sss.pgh.pa.us>
          Jan 30, 2026, 1:22 AM UTC
          Here's a v2 responding to your suggestions. 0001 refactors
          attalignnominal(), and then 0002 is nearly the same as the
          prior patch except for rebasing over that change. I added
          a test case for alignment = max too (the other cases seem
          covered already, somewhat indirectly via pg_upgrade testing).
          I think this might be about ready to go, unless somebody has
          a better idea than 'l' for the catalog representation of
          TYPALIGN_INT64.
          		regards, tom lane
          • Jump to comment-1
            Tom Lane<tgl@sss.pgh.pa.us>
            Jan 31, 2026, 4:46 AM UTC
            I wrote:
            I think this might be about ready to go, unless somebody has
            a better idea than 'l' for the catalog representation of
            TYPALIGN_INT64.
            I realized that there is more to consider here than we'd thought.
            In particular, while we've mainly worried about what happens with
            system catalog row layout, the change I've proposed here very
            likely changes row layout in user tables, if we are on a platform
            where TYPALIGNINT64 != TYPALIGNDOUBLE. So this is a pg_upgrade
            breaking change for such platforms.
            Maybe this is another reason to decide that AIX isn't worth
            re-supporting: if there are any AIX users out there who still care,
            the prospect of a forced dump-and-reload might be enough to convince
            them that they might as well migrate to a more modern platform while
            they are at it. Not sure.
            If we do want to go forward with this, it would make sense to
            adjust pgcontrol to store MAXALIGN, TYPALIGNDOUBLE, and
            TYPALIGN_INT64 separately, instead of
            /*
             * This data is used to check for hardware-architecture compatibility of
             * the database and the backend executable.  We need not check endianness
             * explicitly, since the pg_control version will surely look wrong to a
             * machine of different endianness, but we do need to worry about MAXALIGN
             * and floating-point format.  (Note: storage layout nominally also
             * depends on SHORTALIGN and INTALIGN, but in practice these are the same
             * on all architectures of interest.)
             *
             * Testing just one double value is not a very bulletproof test for
             * floating-point compatibility, but it will catch most cases.
             */
            uint32        maxAlign;        /* alignment requirement for tuples */
            double        floatFormat;    /* constant 1234567.0 */
            #define FLOATFORMAT_VALUE 1234567.0
            (Given that we've now pretty much locked in on IEEE float format,
            I suspect floatFormat isn't pulling its weight anymore, but
            perhaps that's a separate discussion.)
            		regards, tom lane