aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2017-11-14 17:49:49 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2017-11-14 17:49:49 -0500
commit619a8c47da7279c186bb57cc16b26ad011366b73 (patch)
treec5e31b16287e6318da25450301a7b718ed112d36 /src
parenta8910506757c58016616b9b0c4c6c78dcb835e3b (diff)
downloadpostgresql-619a8c47da7279c186bb57cc16b26ad011366b73.tar.gz
postgresql-619a8c47da7279c186bb57cc16b26ad011366b73.zip
Prevent int128 from requiring more than MAXALIGN alignment.
Our initial work with int128 neglected alignment considerations, an oversight that came back to bite us in bug #14897 from Vincent Lachenal. It is unsurprising that int128 might have a 16-byte alignment requirement; what's slightly more surprising is that even notoriously lax Intel chips sometimes enforce that. Raising MAXALIGN seems out of the question: the costs in wasted disk and memory space would be significant, and there would also be an on-disk compatibility break. Nor does it seem very practical to try to allow some data structures to have more-than-MAXALIGN alignment requirement, as we'd have to push knowledge of that throughout various code that copies data structures around. The only way out of the box is to make type int128 conform to the system's alignment assumptions. Fortunately, gcc supports that via its __attribute__(aligned()) pragma; and since we don't currently support int128 on non-gcc-workalike compilers, we shouldn't be losing any platform support this way. Although we could have just done pg_attribute_aligned(MAXIMUM_ALIGNOF) and called it a day, I did a little bit of extra work to make the code more portable than that: it will also support int128 on compilers without __attribute__(aligned()), if the native alignment of their 128-bit-int type is no more than that of int64. Add a regression test case that exercises the one known instance of the problem, in parallel aggregation over a bigint column. Back-patch of commit 751804998. The code known to be affected only exists in 9.6 and later, but we do have some stuff using int128 in 9.5, so patch back to 9.5. Discussion: https://postgr.es/m/20171110185747.31519.28038@wrigleys.postgresql.org
Diffstat (limited to 'src')
-rw-r--r--src/include/c.h26
-rw-r--r--src/include/pg_config.h.in3
-rw-r--r--src/include/pg_config.h.win323
-rw-r--r--src/test/regress/expected/select_parallel.out18
-rw-r--r--src/test/regress/sql/select_parallel.sql6
5 files changed, 51 insertions, 5 deletions
diff --git a/src/include/c.h b/src/include/c.h
index 38c0ed7c304..d4dd86636f7 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -370,13 +370,29 @@ typedef unsigned long long int uint64;
/*
* 128-bit signed and unsigned integers
- * There currently is only a limited support for the type. E.g. 128bit
- * literals and snprintf are not supported; but math is.
+ * There currently is only limited support for such types.
+ * E.g. 128bit literals and snprintf are not supported; but math is.
+ * Also, because we exclude such types when choosing MAXIMUM_ALIGNOF,
+ * it must be possible to coerce the compiler to allocate them on no
+ * more than MAXALIGN boundaries.
*/
#if defined(PG_INT128_TYPE)
-#define HAVE_INT128
-typedef PG_INT128_TYPE int128;
-typedef unsigned PG_INT128_TYPE uint128;
+#if defined(pg_attribute_aligned) || ALIGNOF_PG_INT128_TYPE <= MAXIMUM_ALIGNOF
+#define HAVE_INT128 1
+
+typedef PG_INT128_TYPE int128
+#if defined(pg_attribute_aligned)
+pg_attribute_aligned(MAXIMUM_ALIGNOF)
+#endif
+;
+
+typedef unsigned PG_INT128_TYPE uint128
+#if defined(pg_attribute_aligned)
+pg_attribute_aligned(MAXIMUM_ALIGNOF)
+#endif
+;
+
+#endif
#endif
/*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c65dd7db215..f54bbedac5a 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -27,6 +27,9 @@
/* The normal alignment of `long long int', in bytes. */
#undef ALIGNOF_LONG_LONG_INT
+/* The normal alignment of `PG_INT128_TYPE', in bytes. */
+#undef ALIGNOF_PG_INT128_TYPE
+
/* The normal alignment of `short', in bytes. */
#undef ALIGNOF_SHORT
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 9a876f5f0ad..a5cecb3e07d 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -34,6 +34,9 @@
/* The alignment requirement of a `long long int'. */
#define ALIGNOF_LONG_LONG_INT 8
+/* The normal alignment of `PG_INT128_TYPE', in bytes. */
+#undef ALIGNOF_PG_INT128_TYPE
+
/* The alignment requirement of a `short'. */
#define ALIGNOF_SHORT 2
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index f3e8b565871..ecabd35cca6 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -444,6 +444,24 @@ select string4 from tenk1 order by string4 limit 5;
reset max_parallel_workers;
reset enable_hashagg;
+-- check parallelized int8 aggregate (bug #14897)
+explain (costs off)
+select avg(unique1::int8) from tenk1;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Finalize Aggregate
+ -> Gather
+ Workers Planned: 4
+ -> Partial Aggregate
+ -> Parallel Index Only Scan using tenk1_unique1 on tenk1
+(5 rows)
+
+select avg(unique1::int8) from tenk1;
+ avg
+-----------------------
+ 4999.5000000000000000
+(1 row)
+
-- test the sanity of parallel query after the active role is dropped.
drop role if exists regress_parallel_worker;
NOTICE: role "regress_parallel_worker" does not exist, skipping
diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql
index d2eee147e99..280c7876536 100644
--- a/src/test/regress/sql/select_parallel.sql
+++ b/src/test/regress/sql/select_parallel.sql
@@ -168,6 +168,12 @@ select string4 from tenk1 order by string4 limit 5;
reset max_parallel_workers;
reset enable_hashagg;
+-- check parallelized int8 aggregate (bug #14897)
+explain (costs off)
+select avg(unique1::int8) from tenk1;
+
+select avg(unique1::int8) from tenk1;
+
-- test the sanity of parallel query after the active role is dropped.
drop role if exists regress_parallel_worker;
create role regress_parallel_worker;