Subject: Decoupling our alignment assumptions about int64 and double

From: Tom Lane <[email-masked]>
Date: Wed, 28 Jan 2026 20:20:24 -0500

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 TYPALIGN_DOUBLE into three values, TYPALIGN_DOUBLE
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%';
         typname         | typlen | typalign 
-------------------------+--------+----------
 any                     |      4 | i
 anyarray                |     -1 | m
 anycompatible           |      4 | i
 anycompatiblearray      |     -1 | m
 anycompatiblemultirange |     -1 | m
 anycompatiblenonarray   |      4 | i
 anycompatiblerange      |     -1 | m
 anyelement              |      4 | i
 anyenum                 |      4 | i
 anymultirange           |     -1 | m
 anynonarray             |      4 | i
 anyrange                |     -1 | m
(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

From 4474db54e4c481c7a919062812b33ba2e703d626 Mon Sep 17 00:00:00 2001
From: Tom Lane <[email-masked]>
Date: Wed, 28 Jan 2026 19:54:46 -0500
Subject: [PATCH v1] Decouple our alignment assumptions about int64 and double.

Up to now we have assumed that int64 and double have the same
alignment requirement, but there are platforms on which that's
not true (notably AIX).  This assumption is a bit of a wart anyway,
since it leads to confusion about which alignment setting to use.
Let's break that apart.

We actually need to split TYPALIGN_DOUBLE into three values not
two, because we need a representation for "the maximum alignment
on this platform"; composite types need to use that setting.
So this patch invents TYPALIGN_INT64 and TYPALIGN_MAX, and
propagates those into all the necessary places.  It's pretty
straightforward really, though it's certainly possible I missed
a few places to change.

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 populate_compact_attribute()
which is not so performance-critical.  It might be worth
worrying about the increased cost of att_align_nominal(),
but that macro is not that heavily used IMO.

An issue not resolved here is that we are not consistent
about the pg_type typlen/typalign values for polymorphic
types (the "any*" types).  Those shouldn't actually matter,
but why aren't they all alike?

Side notes:
I got rid of LONGALIGN[_DOWN] along with the configure probes
for ALIGNOF_LONG.  We were not using those anywhere and it
seems highly unlikely that we'd do so in future.
bootstrap.c turns out to have had the wrong alignment value
for _aclitem; seems that was missed when we widened AclMode
to 64 bits.  AFAICT the value wasn't used so there were no
ill effects.
I fixed a couple of places that were using hard-coded values
when they could have used TYPALIGN_xxx macros.

This will require a catversion bump.
---
 configure                                     | 64 +++----------------
 configure.ac                                  | 30 +++------
 doc/src/sgml/catalogs.sgml                    |  8 ++-
 doc/src/sgml/ref/create_type.sgml             |  9 ++-
 meson.build                                   | 31 ++++-----
 src/backend/access/common/tupdesc.c           |  8 ++-
 src/backend/bootstrap/bootstrap.c             |  4 +-
 src/backend/catalog/Catalog.pm                | 10 ++-
 src/backend/catalog/heap.c                    |  4 +-
 src/backend/catalog/pg_type.c                 |  9 ++-
 src/backend/commands/typecmds.c               | 38 +++++++++--
 src/backend/tsearch/ts_typanalyze.c           |  2 +-
 src/backend/utils/adt/arrayfuncs.c            |  2 +-
 src/backend/utils/adt/orderedsetaggs.c        |  2 +-
 src/backend/utils/adt/rangetypes_typanalyze.c |  2 +-
 src/bin/initdb/initdb.c                       |  2 +-
 src/bin/pg_dump/pg_dump.c                     |  4 ++
 src/include/access/tupmacs.h                  | 12 ++--
 src/include/c.h                               |  4 +-
 src/include/catalog/pg_type.dat               | 62 +++++++++---------
 src/include/catalog/pg_type.h                 |  2 +
 src/include/pg_config.h.in                    |  3 -
 src/pl/plpython/plpy_typeio.c                 |  4 +-
 src/test/regress/expected/float8.out          |  8 ++-
 src/test/regress/expected/type_sanity.out     |  8 ++-
 src/test/regress/sql/float8.sql               |  6 +-
 src/test/regress/sql/type_sanity.sql          |  8 ++-
 27 files changed, 177 insertions(+), 169 deletions(-)

diff --git a/configure b/configure
index a10a2c85c6a..5adfb3eba45 100755
--- a/configure
+++ b/configure
@@ -17095,41 +17095,6 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF

-# The cast to long int works around a bug in the HP C Compiler,
-# see AC_CHECK_SIZEOF for more information.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of long" >&5
-$as_echo_n "checking alignment of long... " >&6; }
-if ${ac_cv_alignof_long+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_long"        "$ac_includes_default
-#ifndef offsetof
-# define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0)
-#endif
-typedef struct { char x; long y; } ac__type_alignof_;"; then :
-
-else
-  if test "$ac_cv_type_long" = yes; then
-     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error 77 "cannot compute alignment of long
-See \`config.log' for more details" "$LINENO" 5; }
-   else
-     ac_cv_alignof_long=0
-   fi
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_long" >&5
-$as_echo "$ac_cv_alignof_long" >&6; }
-
-
-
-cat >>confdefs.h <<_ACEOF
-#define ALIGNOF_LONG $ac_cv_alignof_long
-_ACEOF
-
-
 # The cast to long int works around a bug in the HP C Compiler,
 # see AC_CHECK_SIZEOF for more information.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of int64_t" >&5
@@ -17203,27 +17168,16 @@ _ACEOF
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
 
-MAX_ALIGNOF=$ac_cv_alignof_double
-
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long' is greater than the alignment of 'double'" "$LINENO" 5
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'int64_t' is greater than the alignment of 'double'" "$LINENO" 5
+if test $ac_cv_alignof_int64_t -gt $ac_cv_alignof_double ; then
+  MAX_ALIGNOF=$ac_cv_alignof_int64_t
+else
+  MAX_ALIGNOF=$ac_cv_alignof_double
 fi
 
 cat >>confdefs.h <<_ACEOF
diff --git a/configure.ac b/configure.ac
index 814e64a967e..a5eab2e5fff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2031,33 +2031,21 @@ AC_CHECK_SIZEOF([intmax_t])
 
 AC_CHECK_ALIGNOF(short)
 AC_CHECK_ALIGNOF(int)
-AC_CHECK_ALIGNOF(long)
 AC_CHECK_ALIGNOF(int64_t)
 AC_CHECK_ALIGNOF(double)
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
 
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'long' is greater than the alignment of 'double'])
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'int64_t' is greater than the alignment of 'double'])
+if test $ac_cv_alignof_int64_t -gt $ac_cv_alignof_double ; then
+  MAX_ALIGNOF=$ac_cv_alignof_int64_t
+else
+  MAX_ALIGNOF=$ac_cv_alignof_double
 fi
 AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 332193565e2..12608d00a18 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -9567,7 +9567,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
          <para><literal>i</literal> = <type>int</type> alignment (4 bytes on most machines).</para>
         </listitem>
         <listitem>
-         <para><literal>d</literal> = <type>double</type> alignment (8 bytes on many machines, but by no means all).</para>
+         <para><literal>l</literal> = <type>int64</type> alignment (8 bytes on most machines, but by no means all).</para>
+        </listitem>
+        <listitem>
+         <para><literal>d</literal> = <type>double</type> alignment (8 bytes on most machines, but by no means all).</para>
+        </listitem>
+        <listitem>
+         <para><literal>m</literal> = maximum alignment (8 bytes on most machines).</para>
         </listitem>
        </itemizedlist>
       </para></entry>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 994dfc65268..006db624094 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -748,8 +748,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
      <para>
       The storage alignment requirement of the data type.  If specified,
       it must be <literal>char</literal>, <literal>int2</literal>,
-      <literal>int4</literal>, or <literal>double</literal>; the
+      <literal>int4</literal>, <literal>int8</literal>,
+      <literal>double</literal>, or <literal>max</literal>; the
       default is <literal>int4</literal>.
+      <literal>int8</literal> and <literal>double</literal> usually
+      have the same behavior, but there are platforms on which they are
+      different because integer and floating-point values have different
+      requirements.  <literal>max</literal> means the maximum alignment
+      requirement for any of these types; it is often used for container
+      types such as records.
      </para>
     </listitem>
    </varlistentry>
diff --git a/meson.build b/meson.build
index df907b62da3..bb17859fee4 100644
--- a/meson.build
+++ b/meson.build
@@ -1798,32 +1798,27 @@ endif
 
 # Determine memory alignment requirements for the basic C data types.
 
-alignof_types = ['short', 'int', 'long', 'double']
+alignof_types = ['short', 'int', 'int64_t', 'double']
 foreach t : alignof_types
-  align = cc.alignment(t, args: test_c_args)
+  align = cc.alignment(t, args: test_c_args, prefix: '#include <stdint.h>')
   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
 endforeach
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that int64_t's alignment is at least as strong
-# as long, char, short, or int.  Note that we intentionally do not consider
-# any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
-# would be too much of a penalty for disk and memory space.
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
+
+alignof_int64_t = cdata.get('ALIGNOF_INT64_T')
 alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment('int64_t', args: test_c_args, prefix: '#include <stdint.h>') > alignof_double
-  error('alignment of int64_t is greater than the alignment of double')
+if alignof_int64_t > alignof_double
+  cdata.set('MAXIMUM_ALIGNOF', alignof_int64_t)
+else
+  cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 endif
-cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 
 cdata.set('SIZEOF_LONG', cc.sizeof('long', args: test_c_args))
 cdata.set('SIZEOF_LONG_LONG', cc.sizeof('long long', args: test_c_args))
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 94b4f1f9975..49e5cd6c117 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -94,9 +94,15 @@ populate_compact_attribute_internal(Form_pg_attribute src,
 		case TYPALIGN_CHAR:
 			dst->attalignby = sizeof(char);
 			break;
+		case TYPALIGN_INT64:
+			dst->attalignby = ALIGNOF_INT64_T;
+			break;
 		case TYPALIGN_DOUBLE:
 			dst->attalignby = ALIGNOF_DOUBLE;
 			break;
+		case TYPALIGN_MAX:
+			dst->attalignby = MAXIMUM_ALIGNOF;
+			break;
 		case TYPALIGN_SHORT:
 			dst->attalignby = ALIGNOF_SHORT;
 			break;
@@ -994,7 +1000,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
 		case INT8OID:
 			att->attlen = 8;
 			att->attbyval = true;
-			att->attalign = TYPALIGN_DOUBLE;
+			att->attalign = TYPALIGN_INT64;
 			att->attstorage = TYPSTORAGE_PLAIN;
 			att->attcompression = InvalidCompressionMethod;
 			att->attcollation = InvalidOid;
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index dd57624b4f9..7a3a13a6ec7 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -115,7 +115,7 @@ static const struct typinfo TypInfo[] = {
 	F_TEXTIN, F_TEXTOUT},
 	{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_OIDIN, F_OIDOUT},
-	{"oid8", OID8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
+	{"oid8", OID8OID, 0, 8, true, TYPALIGN_INT64, TYPSTORAGE_PLAIN, InvalidOid,
 	F_OID8IN, F_OID8OUT},
 	{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_TIDIN, F_TIDOUT},
@@ -137,7 +137,7 @@ static const struct typinfo TypInfo[] = {
 	F_ARRAY_IN, F_ARRAY_OUT},
 	{"_char", 1002, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
 	F_ARRAY_IN, F_ARRAY_OUT},
-	{"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
+	{"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT64, TYPSTORAGE_EXTENDED, InvalidOid,
 	F_ARRAY_IN, F_ARRAY_OUT}
 };
 
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 219af5884d9..20884f45bd8 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -464,9 +464,13 @@ sub GenerateArrayTypes
 		$array_type{typname} = '_' . $elem_type->{typname};
 		$array_type{typelem} = $elem_type->{typname};
 
-		# Arrays require INT alignment, unless the element type requires
-		# DOUBLE alignment.
-		$array_type{typalign} = $elem_type->{typalign} eq 'd' ? 'd' : 'i';
+		# Arrays require INT alignment, unless the element type requires more.
+		$array_type{typalign} =
+		  $elem_type->{typalign} eq 'l' ? 'l'
+		  : (
+			$elem_type->{typalign} eq 'd' ? 'd'
+			: ( $elem_type->{typalign} eq 'm' ? 'm'
+				: 'i'));
 
 		# Fill in the rest of the array entry's fields.
 		foreach my $column (@$pgtype_schema)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 606434823cf..27cc09a7680 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1075,7 +1075,7 @@ AddNewRelationType(const char *typeName,
 				   NULL,		/* default value - none */
 				   NULL,		/* default binary representation */
 				   false,		/* passed by reference */
-				   TYPALIGN_DOUBLE, /* alignment - must be the largest! */
+				   TYPALIGN_MAX,	/* alignment - must be the largest! */
 				   TYPSTORAGE_EXTENDED, /* fully TOASTable */
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
@@ -1399,7 +1399,7 @@ heap_create_with_catalog(const char *relname,
 				   NULL,		/* default value - none */
 				   NULL,		/* default binary representation */
 				   false,		/* passed by reference */
-				   TYPALIGN_DOUBLE, /* alignment - must be the largest! */
+				   TYPALIGN_MAX,	/* alignment - must be the largest! */
 				   TYPSTORAGE_EXTENDED, /* fully TOASTable */
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index fc369c35aa6..f079fa32b40 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -287,7 +287,9 @@ TypeCreate(Oid newTypeOid,
 		}
 		else if (internalSize == (int16) sizeof(int64))
 		{
-			if (alignment != TYPALIGN_DOUBLE)
+			/* We have to trust the user to get this distinction right ... */
+			if (!(alignment == TYPALIGN_INT64 ||
+				  alignment == TYPALIGN_DOUBLE))
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
@@ -303,7 +305,10 @@ TypeCreate(Oid newTypeOid,
 	{
 		/* varlena types must have int align or better */
 		if (internalSize == -1 &&
-			!(alignment == TYPALIGN_INT || alignment == TYPALIGN_DOUBLE))
+			!(alignment == TYPALIGN_INT ||
+			  alignment == TYPALIGN_INT64 ||
+			  alignment == TYPALIGN_DOUBLE ||
+			  alignment == TYPALIGN_MAX))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("alignment \"%c\" is invalid for variable-length type",
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 288edb25f2f..2f919f764aa 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -117,6 +117,7 @@ static void makeMultirangeConstructors(const char *name, Oid namespace,
 									   Oid multirangeOid, Oid rangeOid,
 									   Oid rangeArrayOid,
 									   Oid *mltrngConstruct0_p, Oid *mltrngConstruct1_p, Oid *mltrngConstruct2_p);
+static char containerAlignment(char elemalign);
 static Oid	findTypeInputFunction(List *procname, Oid typeOid);
 static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -424,6 +425,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 			pg_strcasecmp(a, "float8") == 0 ||
 			pg_strcasecmp(a, "pg_catalog.float8") == 0)
 			alignment = TYPALIGN_DOUBLE;
+		else if (pg_strcasecmp(a, "int8") == 0 ||
+				 pg_strcasecmp(a, "pg_catalog.int8") == 0)
+			alignment = TYPALIGN_INT64;
 		else if (pg_strcasecmp(a, "int4") == 0 ||
 				 pg_strcasecmp(a, "pg_catalog.int4") == 0)
 			alignment = TYPALIGN_INT;
@@ -433,6 +437,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		else if (pg_strcasecmp(a, "char") == 0 ||
 				 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
 			alignment = TYPALIGN_CHAR;
+		else if (pg_strcasecmp(a, "max") == 0 ||
+				 pg_strcasecmp(a, "pg_catalog.max") == 0)
+			alignment = TYPALIGN_MAX;
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -611,8 +618,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 	 */
 	array_type = makeArrayTypeName(typeName, typeNamespace);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
-	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for arrays */
+	alignment = containerAlignment(alignment);
 
 	TypeCreate(array_oid,		/* force assignment of this type OID */
 			   array_type,		/* type name */
@@ -1095,8 +1102,8 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 	 */
 	domainArrayName = makeArrayTypeName(domainName, domainNamespace);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
-	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for arrays */
+	alignment = containerAlignment(alignment);
 
 	TypeCreate(domainArrayOid,	/* force assignment of this type OID */
 			   domainArrayName, /* type name */
@@ -1557,8 +1564,8 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
 	get_typlenbyvalalign(rangeSubtype,
 						 &subtyplen, &subtypbyval, &subtypalign);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
-	alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for ranges */
+	alignment = containerAlignment(subtypalign);
 
 	/* Allocate OID for array type, its multirange, and its multirange array */
 	rangeArrayOid = AssignTypeArrayOid();
@@ -2005,6 +2012,25 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	pfree(parameterModes);
 }
 
+/*
+ * Compute the appropriate typalign for a container (array or range)
+ * given the typalign of its members.  The container type requires at
+ * least int alignment, but more if the members need more.
+ */
+static char
+containerAlignment(char elemalign)
+{
+	switch (elemalign)
+	{
+		case TYPALIGN_INT64:
+		case TYPALIGN_DOUBLE:
+		case TYPALIGN_MAX:
+			return elemalign;
+		default:
+			return TYPALIGN_INT;
+	}
+}
+
 /*
  * Find suitable I/O and other support functions for a type.
  *
diff --git a/src/backend/tsearch/ts_typanalyze.c b/src/backend/tsearch/ts_typanalyze.c
index 0c513d694e7..48ee050e37f 100644
--- a/src/backend/tsearch/ts_typanalyze.c
+++ b/src/backend/tsearch/ts_typanalyze.c
@@ -444,7 +444,7 @@ compute_tsvector_stats(VacAttrStats *stats,
 			stats->statypid[0] = TEXTOID;
 			stats->statyplen[0] = -1;	/* typlen, -1 for varlena */
 			stats->statypbyval[0] = false;
-			stats->statypalign[0] = 'i';
+			stats->statypalign[0] = TYPALIGN_INT;
 		}
 	}
 	else
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index e71d32773b5..0d185f5be8a 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3426,7 +3426,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 		case INT8OID:
 			elmlen = sizeof(int64);
 			elmbyval = true;
-			elmalign = TYPALIGN_DOUBLE;
+			elmalign = TYPALIGN_INT64;
 			break;
 
 		case NAMEOID:
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 3b6da8e36ac..27b8cfa7064 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -1021,7 +1021,7 @@ percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
 	return percentile_cont_multi_final_common(fcinfo,
 											  INTERVALOID,
 	/* hard-wired info on type interval */
-											  16, false, TYPALIGN_DOUBLE,
+											  16, false, TYPALIGN_INT64,
 											  interval_lerp);
 }
 
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index 38d12dedbc5..278d4e6941a 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -398,7 +398,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 		stats->statypid[slot_idx] = FLOAT8OID;
 		stats->statyplen[slot_idx] = sizeof(float8);
 		stats->statypbyval[slot_idx] = true;
-		stats->statypalign[slot_idx] = 'd';
+		stats->statypalign[slot_idx] = TYPALIGN_DOUBLE;
 
 		/* Store the fraction of empty ranges */
 		emptyfrac = palloc_object(float4);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index a3980e5535f..e550e9cf0a3 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1580,7 +1580,7 @@ bootstrap_template1(void)
 	bki_lines = replace_token(bki_lines, "SIZEOF_POINTER", buf);
 
 	bki_lines = replace_token(bki_lines, "ALIGNOF_POINTER",
-							  (sizeof(Pointer) == 4) ? "i" : "d");
+							  (sizeof(Pointer) == 4) ? "i" : "l");
 
 	bki_lines = replace_token(bki_lines, "POSTGRES",
 							  escape_quotes_bki(username));
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 078ee8500ad..b94121ceb32 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12652,8 +12652,12 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int2");
 	else if (*typalign == TYPALIGN_INT)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int4");
+	else if (*typalign == TYPALIGN_INT64)
+		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int8");
 	else if (*typalign == TYPALIGN_DOUBLE)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = double");
+	else if (*typalign == TYPALIGN_MAX)
+		appendPQExpBufferStr(q, ",\n    ALIGNMENT = max");
 
 	if (*typstorage == TYPSTORAGE_PLAIN)
 		appendPQExpBufferStr(q, ",\n    STORAGE = plain");
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 3e5530658c9..c5462d6fba1 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -146,11 +146,13 @@ fetch_att(const void *T, bool attbyval, int attlen)
 ( \
 	((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \
 	 (((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \
-	  (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
-	   ( \
-			AssertMacro((attalign) == TYPALIGN_SHORT), \
-			SHORTALIGN(cur_offset) \
-	   ))) \
+	  (((attalign) == TYPALIGN_INT64) ? INT64ALIGN(cur_offset) : \
+	   (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
+		(((attalign) == TYPALIGN_MAX) ? MAXALIGN(cur_offset) : \
+		 ( \
+			 AssertMacro((attalign) == TYPALIGN_SHORT), \
+			 SHORTALIGN(cur_offset) \
+			 ))))) \
 )
 
 /*
diff --git a/src/include/c.h b/src/include/c.h
index 48e4087c09c..f802633bde4 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -821,7 +821,7 @@ typedef NameData *Name;
 
 #define SHORTALIGN(LEN)			TYPEALIGN(ALIGNOF_SHORT, (LEN))
 #define INTALIGN(LEN)			TYPEALIGN(ALIGNOF_INT, (LEN))
-#define LONGALIGN(LEN)			TYPEALIGN(ALIGNOF_LONG, (LEN))
+#define INT64ALIGN(LEN)			TYPEALIGN(ALIGNOF_INT64_T, (LEN))
 #define DOUBLEALIGN(LEN)		TYPEALIGN(ALIGNOF_DOUBLE, (LEN))
 #define MAXALIGN(LEN)			TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
 /* MAXALIGN covers only built-in types, not buffers */
@@ -833,7 +833,7 @@ typedef NameData *Name;
 
 #define SHORTALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN))
 #define INTALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_INT, (LEN))
-#define LONGALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN))
+#define INT64ALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_INT64_T, (LEN))
 #define DOUBLEALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN))
 #define MAXALIGN_DOWN(LEN)		TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN))
 #define BUFFERALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_BUFFER, (LEN))
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1a753d1797..d511493f761 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -56,7 +56,7 @@
   descr => '~18 digit integer, 8-byte storage',
   typname => 'int8', typlen => '8', typbyval => 't',
   typcategory => 'N', typinput => 'int8in', typoutput => 'int8out',
-  typreceive => 'int8recv', typsend => 'int8send', typalign => 'd' },
+  typreceive => 'int8recv', typsend => 'int8send', typalign => 'l' },
 { oid => '21', array_type_oid => '1005',
   descr => '-32 thousand to 32 thousand, 2-byte storage',
   typname => 'int2', typlen => '2', typbyval => 't', typcategory => 'N',
@@ -116,22 +116,22 @@
   typname => 'pg_type', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_type', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '75', array_type_oid => '270',
   typname => 'pg_attribute', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_attribute', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '81', array_type_oid => '272',
   typname => 'pg_proc', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_proc', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '83', array_type_oid => '273',
   typname => 'pg_class', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_class', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 
 # OIDS 100 - 199
 
@@ -174,7 +174,7 @@
 { oid => '5069', array_type_oid => '271', descr => 'full transaction id',
   typname => 'xid8', typlen => '8', typbyval => 't',
   typcategory => 'U', typinput => 'xid8in', typoutput => 'xid8out',
-  typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'd' },
+  typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'l' },
 
 # OIDS 600 - 699
 
@@ -239,7 +239,7 @@
   descr => 'monetary amounts, $d,ddd.cc',
   typname => 'money', typlen => '8', typbyval => 't',
   typcategory => 'N', typinput => 'cash_in', typoutput => 'cash_out',
-  typreceive => 'cash_recv', typsend => 'cash_send', typalign => 'd' },
+  typreceive => 'cash_recv', typsend => 'cash_send', typalign => 'l' },
 
 # OIDS 800 - 899
 
@@ -270,7 +270,7 @@
 { oid => '1033', array_type_oid => '1034', descr => 'access control list',
   typname => 'aclitem', typlen => '16', typbyval => 'f', typcategory => 'U',
   typinput => 'aclitemin', typoutput => 'aclitemout', typreceive => '-',
-  typsend => '-', typalign => 'd' },
+  typsend => '-', typalign => 'l' },
 { oid => '1042', array_type_oid => '1014',
   descr => '\'char(length)\' blank-padded string, fixed storage length',
   typname => 'bpchar', typlen => '-1', typbyval => 'f', typcategory => 'S',
@@ -293,7 +293,7 @@
   typname => 'time', typlen => '8', typbyval => 't',
   typcategory => 'D', typinput => 'time_in', typoutput => 'time_out',
   typreceive => 'time_recv', typsend => 'time_send', typmodin => 'timetypmodin',
-  typmodout => 'timetypmodout', typalign => 'd' },
+  typmodout => 'timetypmodout', typalign => 'l' },
 
 # OIDS 1100 - 1199
 
@@ -302,21 +302,21 @@
   typcategory => 'D', typinput => 'timestamp_in', typoutput => 'timestamp_out',
   typreceive => 'timestamp_recv', typsend => 'timestamp_send',
   typmodin => 'timestamptypmodin', typmodout => 'timestamptypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 { oid => '1184', array_type_oid => '1185',
   descr => 'date and time with time zone',
   typname => 'timestamptz', typlen => '8', typbyval => 't',
   typcategory => 'D', typispreferred => 't', typinput => 'timestamptz_in',
   typoutput => 'timestamptz_out', typreceive => 'timestamptz_recv',
   typsend => 'timestamptz_send', typmodin => 'timestamptztypmodin',
-  typmodout => 'timestamptztypmodout', typalign => 'd' },
+  typmodout => 'timestamptztypmodout', typalign => 'l' },
 { oid => '1186', array_type_oid => '1187',
   descr => 'time interval, format \'number units ...\'',
   typname => 'interval', typlen => '16', typbyval => 'f', typcategory => 'T',
   typispreferred => 't', typinput => 'interval_in', typoutput => 'interval_out',
   typreceive => 'interval_recv', typsend => 'interval_send',
   typmodin => 'intervaltypmodin', typmodout => 'intervaltypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 
 # OIDS 1200 - 1299
 
@@ -326,7 +326,7 @@
   typinput => 'timetz_in', typoutput => 'timetz_out',
   typreceive => 'timetz_recv', typsend => 'timetz_send',
   typmodin => 'timetztypmodin', typmodout => 'timetztypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 
 # OIDS 1500 - 1599
 
@@ -415,7 +415,7 @@
 { oid => '3220', array_type_oid => '3221', descr => 'PostgreSQL LSN',
   typname => 'pg_lsn', typlen => '8', typbyval => 't',
   typcategory => 'U', typinput => 'pg_lsn_in', typoutput => 'pg_lsn_out',
-  typreceive => 'pg_lsn_recv', typsend => 'pg_lsn_send', typalign => 'd' },
+  typreceive => 'pg_lsn_recv', typsend => 'pg_lsn_send', typalign => 'l' },
 
 # text search
 { oid => '3614', array_type_oid => '3643',
@@ -462,12 +462,12 @@
   typname => 'txid_snapshot', typlen => '-1', typbyval => 'f',
   typcategory => 'U', typinput => 'txid_snapshot_in',
   typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
-  typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'txid_snapshot_send', typalign => 'l', typstorage => 'x' },
 { oid => '5038', array_type_oid => '5039', descr => 'transaction snapshot',
   typname => 'pg_snapshot', typlen => '-1', typbyval => 'f', typcategory => 'U',
   typinput => 'pg_snapshot_in', typoutput => 'pg_snapshot_out',
   typreceive => 'pg_snapshot_recv', typsend => 'pg_snapshot_send',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 
 # range types
 { oid => '3904', array_type_oid => '3905', descr => 'range of integers',
@@ -485,13 +485,13 @@
   typname => 'tsrange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 { oid => '3910', array_type_oid => '3911',
   descr => 'range of timestamps with time zone',
   typname => 'tstzrange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 { oid => '3912', array_type_oid => '3913', descr => 'range of dates',
   typname => 'daterange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
@@ -501,7 +501,7 @@
   typname => 'int8range', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 
 # multirange types
 { oid => '4451', array_type_oid => '6150', descr => 'multirange of integers',
@@ -522,14 +522,14 @@
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 { oid => '4534', array_type_oid => '6153',
   descr => 'multirange of timestamps with time zone',
   typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 { oid => '4535', array_type_oid => '6155', descr => 'multirange of dates',
   typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
   typcategory => 'R', typinput => 'multirange_in',
@@ -541,7 +541,7 @@
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 
 # pseudo-types
 # types with typtype='p' represent various special cases in the type system.
@@ -556,14 +556,14 @@
   typname => 'record', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typarray => '_record', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 # Arrays of records have typcategory P, so they can't be autogenerated.
 { oid => '2287',
   typname => '_record', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typsubscript => 'array_subscript_handler',
   typelem => 'record', typinput => 'array_in', typoutput => 'array_out',
   typreceive => 'array_recv', typsend => 'array_send',
-  typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'array_typanalyze', typalign => 'm', typstorage => 'x' },
 { oid => '2275', array_type_oid => '1263', descr => 'C-style string',
   typname => 'cstring', typlen => '-2', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'cstring_in', typoutput => 'cstring_out',
@@ -575,7 +575,7 @@
 { oid => '2277', descr => 'pseudo-type representing a polymorphic array type',
   typname => 'anyarray', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anyarray_in', typoutput => 'anyarray_out',
-  typreceive => 'anyarray_recv', typsend => 'anyarray_send', typalign => 'd',
+  typreceive => 'anyarray_recv', typsend => 'anyarray_send', typalign => 'm',
   typstorage => 'x' },
 { oid => '2278',
   descr => 'pseudo-type for the result of a function with no real result',
@@ -648,7 +648,7 @@
   descr => 'pseudo-type representing a range over a polymorphic base type',
   typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
-  typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+  typreceive => '-', typsend => '-', typalign => 'm', typstorage => 'x' },
 { oid => '5077',
   descr => 'pseudo-type representing a polymorphic common type',
   typname => 'anycompatible', typlen => '4', typbyval => 't', typtype => 'p',
@@ -661,7 +661,7 @@
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblearray_in',
   typoutput => 'anycompatiblearray_out',
   typreceive => 'anycompatiblearray_recv', typsend => 'anycompatiblearray_send',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '5079',
   descr => 'pseudo-type representing a polymorphic common type that is not an array',
   typname => 'anycompatiblenonarray', typlen => '4', typbyval => 't',
@@ -673,19 +673,19 @@
   typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
   typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4537',
   descr => 'pseudo-type representing a polymorphic base type that is a multirange',
   typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anymultirange_in',
   typoutput => 'anymultirange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4538',
   descr => 'pseudo-type representing a multirange over a polymorphic common type',
   typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
   typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4600', descr => 'pseudo-type representing BRIN bloom summary',
   typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f',
   typcategory => 'Z', typinput => 'brin_bloom_summary_in',
@@ -704,5 +704,5 @@
   descr => 'object identifier(oid8), 8 bytes',
   typname => 'oid8', typlen => '8', typbyval => 't', typcategory => 'N',
   typinput => 'oid8in', typoutput => 'oid8out', typreceive => 'oid8recv',
-  typsend => 'oid8send', typalign => 'd' },
+  typsend => 'oid8send', typalign => 'l' },
 ]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 70d4a20c02b..b749a47d651 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -302,7 +302,9 @@ MAKE_SYSCACHE(TYPENAMENSP, pg_type_typname_nsp_index, 64);
 #define  TYPALIGN_CHAR			'c' /* char alignment (i.e. unaligned) */
 #define  TYPALIGN_SHORT			's' /* short alignment (typically 2 bytes) */
 #define  TYPALIGN_INT			'i' /* int alignment (typically 4 bytes) */
+#define  TYPALIGN_INT64			'l' /* int64 alignment (often 8 bytes) */
 #define  TYPALIGN_DOUBLE		'd' /* double alignment (often 8 bytes) */
+#define  TYPALIGN_MAX			'm' /* maximum alignment (usually 8 bytes) */
 
 #define  TYPSTORAGE_PLAIN		'p' /* type not prepared for toasting */
 #define  TYPSTORAGE_EXTERNAL	'e' /* toastable, don't try to compress */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 339268dc8ef..5699c033ade 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -12,9 +12,6 @@
 /* The normal alignment of `int64_t', in bytes. */
 #undef ALIGNOF_INT64_T
 
-/* The normal alignment of `long', in bytes. */
-#undef ALIGNOF_LONG
-
 /* The normal alignment of `PG_INT128_TYPE', in bytes. */
 #undef ALIGNOF_PG_INT128_TYPE
 
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 1f69109b081..91e40327788 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -329,7 +329,7 @@ PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt,
 		/* hard-wired knowledge about type RECORD: */
 		arg->typbyval = false;
 		arg->typlen = -1;
-		arg->typalign = TYPALIGN_DOUBLE;
+		arg->typalign = TYPALIGN_MAX;
 	}
 
 	/*
@@ -452,7 +452,7 @@ PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt,
 		/* hard-wired knowledge about type RECORD: */
 		arg->typbyval = false;
 		arg->typlen = -1;
-		arg->typalign = TYPALIGN_DOUBLE;
+		arg->typalign = TYPALIGN_MAX;
 	}
 
 	/*
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 9c519f1a1a1..a688cbc3c83 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1088,8 +1088,10 @@ LINE 1: ...8 (input = xfloat8in, output = xfloat8out, like = no_such_ty...
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
-create cast (xfloat8 as bigint) without function;
-create cast (bigint as xfloat8) without function;
+-- this hack depends on bigint and float8 having the same pass-by-value-ness:
+create function bigint_xfloat8(bigint) returns xfloat8 immutable strict
+  language internal as 'int8up';
+create cast (bigint as xfloat8) with function bigint_xfloat8(bigint);
 -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4)
 -- we don't care to assume the platform's strtod() handles subnormals
 -- correctly; those are "use at your own risk". However we do test
@@ -1503,5 +1505,5 @@ DETAIL:  drop cascades to function xfloat8in(cstring)
 drop cascades to function xfloat8out(xfloat8)
 drop cascades to cast from xfloat8 to double precision
 drop cascades to cast from double precision to xfloat8
-drop cascades to cast from xfloat8 to bigint
+drop cascades to function bigint_xfloat8(bigint)
 drop cascades to cast from bigint to xfloat8
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 1d21d3eb446..936fa36a600 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -23,7 +23,7 @@ WHERE t1.typnamespace = 0 OR
     (t1.typlen <= 0 AND t1.typlen != -1 AND t1.typlen != -2) OR
     (t1.typtype not in ('b', 'c', 'd', 'e', 'm', 'p', 'r')) OR
     NOT t1.typisdefined OR
-    (t1.typalign not in ('c', 's', 'i', 'd')) OR
+    (t1.typalign not in ('c', 's', 'i', 'l', 'd', 'm')) OR
     (t1.typstorage not in ('p', 'x', 'e', 'm'));
  oid | typname 
 -----+---------
@@ -36,7 +36,7 @@ WHERE t1.typbyval AND
     (t1.typlen != 1 OR t1.typalign != 'c') AND
     (t1.typlen != 2 OR t1.typalign != 's') AND
     (t1.typlen != 4 OR t1.typalign != 'i') AND
-    (t1.typlen != 8 OR t1.typalign != 'd');
+    (t1.typlen != 8 OR (t1.typalign != 'l' AND t1.typalign != 'd'));
  oid | typname 
 -----+---------
 (0 rows)
@@ -108,6 +108,8 @@ FROM pg_type as t1
      LEFT JOIN pg_type as t2 ON rngsubtype = t2.oid
 WHERE t1.typtype = 'r' AND
     (t1.typalign != (CASE WHEN t2.typalign = 'd' THEN 'd'::"char"
+                          WHEN t2.typalign = 'l' THEN 'l'::"char"
+                          WHEN t2.typalign = 'm' THEN 'm'::"char"
                           ELSE 'i'::"char" END)
      OR t2.oid IS NULL);
  oid | typname | typalign | typname | typalign 
@@ -423,6 +425,8 @@ SELECT t1.oid, t1.typname, t1.typalign, t2.typname, t2.typalign
 FROM pg_type AS t1, pg_type AS t2
 WHERE t1.typarray = t2.oid AND
     t2.typalign != (CASE WHEN t1.typalign = 'd' THEN 'd'::"char"
+                         WHEN t1.typalign = 'l' THEN 'l'::"char"
+                         WHEN t1.typalign = 'm' THEN 'm'::"char"
                          ELSE 'i'::"char" END);
  oid | typname | typalign | typname | typalign 
 -----+---------+----------+---------+----------
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 0ef271f2702..c6b59a322ab 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -355,8 +355,10 @@ create type xfloat8 (input = xfloat8in, output = xfloat8out, like = no_such_type
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
-create cast (xfloat8 as bigint) without function;
-create cast (bigint as xfloat8) without function;
+-- this hack depends on bigint and float8 having the same pass-by-value-ness:
+create function bigint_xfloat8(bigint) returns xfloat8 immutable strict
+  language internal as 'int8up';
+create cast (bigint as xfloat8) with function bigint_xfloat8(bigint);
 
 -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4)
 
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 95d5b6e0915..625305c42bc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -28,7 +28,7 @@ WHERE t1.typnamespace = 0 OR
     (t1.typlen <= 0 AND t1.typlen != -1 AND t1.typlen != -2) OR
     (t1.typtype not in ('b', 'c', 'd', 'e', 'm', 'p', 'r')) OR
     NOT t1.typisdefined OR
-    (t1.typalign not in ('c', 's', 'i', 'd')) OR
+    (t1.typalign not in ('c', 's', 'i', 'l', 'd', 'm')) OR
     (t1.typstorage not in ('p', 'x', 'e', 'm'));
 
 -- Look for "pass by value" types that can't be passed by value.
@@ -39,7 +39,7 @@ WHERE t1.typbyval AND
     (t1.typlen != 1 OR t1.typalign != 'c') AND
     (t1.typlen != 2 OR t1.typalign != 's') AND
     (t1.typlen != 4 OR t1.typalign != 'i') AND
-    (t1.typlen != 8 OR t1.typalign != 'd');
+    (t1.typlen != 8 OR (t1.typalign != 'l' AND t1.typalign != 'd'));
 
 -- Look for "toastable" types that aren't varlena.
 
@@ -90,6 +90,8 @@ FROM pg_type as t1
      LEFT JOIN pg_type as t2 ON rngsubtype = t2.oid
 WHERE t1.typtype = 'r' AND
     (t1.typalign != (CASE WHEN t2.typalign = 'd' THEN 'd'::"char"
+                          WHEN t2.typalign = 'l' THEN 'l'::"char"
+                          WHEN t2.typalign = 'm' THEN 'm'::"char"
                           ELSE 'i'::"char" END)
      OR t2.oid IS NULL);
 
@@ -302,6 +304,8 @@ SELECT t1.oid, t1.typname, t1.typalign, t2.typname, t2.typalign
 FROM pg_type AS t1, pg_type AS t2
 WHERE t1.typarray = t2.oid AND
     t2.typalign != (CASE WHEN t1.typalign = 'd' THEN 'd'::"char"
+                         WHEN t1.typalign = 'l' THEN 'l'::"char"
+                         WHEN t1.typalign = 'm' THEN 'm'::"char"
                          ELSE 'i'::"char" END);
 
 -- Check for typelem set without a handler
-- 
2.43.7