summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/tupdesc.c2
-rw-r--r--src/backend/access/gin/gininsert.c5
-rw-r--r--src/backend/access/gist/gistproc.c10
-rw-r--r--src/backend/access/index/indexam.c5
-rw-r--r--src/backend/access/nbtree/nbtcompare.c20
-rw-r--r--src/backend/access/transam/xlog.c20
-rw-r--r--src/backend/catalog/genbki.pl3
-rw-r--r--src/backend/catalog/pg_type.c4
-rw-r--r--src/backend/optimizer/plan/planagg.c2
-rw-r--r--src/backend/optimizer/plan/planner.c4
-rw-r--r--src/backend/parser/parse_node.c2
-rw-r--r--src/backend/port/sysv_sema.c46
-rw-r--r--src/backend/rewrite/rewriteSearchCycle.c2
-rw-r--r--src/backend/storage/ipc/ipc.c2
-rw-r--r--src/backend/utils/adt/arrayfuncs.c6
-rw-r--r--src/backend/utils/adt/int8.c75
-rw-r--r--src/backend/utils/adt/mac.c27
-rw-r--r--src/backend/utils/adt/network.c54
-rw-r--r--src/backend/utils/adt/numeric.c191
-rw-r--r--src/backend/utils/adt/orderedsetaggs.c2
-rw-r--r--src/backend/utils/adt/rangetypes_typanalyze.c2
-rw-r--r--src/backend/utils/adt/timestamp.c21
-rw-r--r--src/backend/utils/adt/uuid.c6
-rw-r--r--src/backend/utils/adt/varlena.c27
-rw-r--r--src/backend/utils/fmgr/fmgr.c35
-rw-r--r--src/backend/utils/resowner/resowner.c7
-rw-r--r--src/backend/utils/sort/tuplesort.c8
-rw-r--r--src/bin/initdb/initdb.c3
-rw-r--r--src/bin/pg_resetwal/pg_resetwal.c2
-rw-r--r--src/include/access/gin_tuple.h4
-rw-r--r--src/include/access/spgist_private.h14
-rw-r--r--src/include/access/tupmacs.h15
-rw-r--r--src/include/c.h8
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_type.dat16
-rw-r--r--src/include/fmgr.h2
-rw-r--r--src/include/nodes/nodes.h8
-rw-r--r--src/include/pg_config_manual.h13
-rw-r--r--src/include/port/pg_bswap.h17
-rw-r--r--src/include/postgres.h79
-rw-r--r--src/include/utils/sortsupport.h4
-rw-r--r--src/interfaces/libpq/fe-connect.c10
-rw-r--r--src/test/isolation/expected/index-killtuples.out355
-rw-r--r--src/test/isolation/isolation_schedule1
-rw-r--r--src/test/isolation/specs/index-killtuples.spec127
45 files changed, 686 insertions, 582 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index be60005ae46..568edacb9bd 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -993,7 +993,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
case INT8OID:
att->attlen = 8;
- att->attbyval = FLOAT8PASSBYVAL;
+ att->attbyval = true;
att->attalign = TYPALIGN_DOUBLE;
att->attstorage = TYPSTORAGE_PLAIN;
att->attcompression = InvalidCompressionMethod;
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index 47b1898a064..e9d4b27427e 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -2189,7 +2189,10 @@ typedef struct
* we simply copy the whole Datum, so that we don't have to care about stuff
* like endianess etc. We could make it a little bit smaller, but it's not
* worth it - it's a tiny fraction of the data, and we need to MAXALIGN the
- * start of the TID list anyway. So we wouldn't save anything.
+ * start of the TID list anyway. So we wouldn't save anything. (This would
+ * not be a good idea for the permanent in-index data, since we'd prefer
+ * that that not depend on sizeof(Datum). But this is just a transient
+ * representation to use while sorting the data.)
*
* The TID list is serialized as compressed - it's highly compressible, and
* we already have ginCompressPostingList for this purpose. The list may be
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index 392163cb229..f2ec6cbe2e5 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -1707,8 +1707,8 @@ gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup)
* Abbreviated version of Z-order comparison
*
* The abbreviated format is a Z-order value computed from the two 32-bit
- * floats. If SIZEOF_DATUM == 8, the 64-bit Z-order value fits fully in the
- * abbreviated Datum, otherwise use its most significant bits.
+ * floats. Now that sizeof(Datum) is always 8, the 64-bit Z-order value
+ * always fits fully in the abbreviated Datum.
*/
static Datum
gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup)
@@ -1718,11 +1718,7 @@ gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup)
z = point_zorder_internal(p->x, p->y);
-#if SIZEOF_DATUM == 8
- return (Datum) z;
-#else
- return (Datum) (z >> 32);
-#endif
+ return UInt64GetDatum(z);
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 219df1971da..1a4f36fe0a9 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -986,11 +986,6 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
{
if (orderByTypes[i] == FLOAT8OID)
{
-#ifndef USE_FLOAT8_BYVAL
- /* must free any old value to avoid memory leakage */
- if (!scan->xs_orderbynulls[i])
- pfree(DatumGetPointer(scan->xs_orderbyvals[i]));
-#endif
if (distances && !distances[i].isnull)
{
scan->xs_orderbyvals[i] = Float8GetDatum(distances[i].value);
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index e1b52acd20d..188c27b4925 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -278,32 +278,12 @@ btint8cmp(PG_FUNCTION_ARGS)
PG_RETURN_INT32(A_LESS_THAN_B);
}
-#if SIZEOF_DATUM < 8
-static int
-btint8fastcmp(Datum x, Datum y, SortSupport ssup)
-{
- int64 a = DatumGetInt64(x);
- int64 b = DatumGetInt64(y);
-
- if (a > b)
- return A_GREATER_THAN_B;
- else if (a == b)
- return 0;
- else
- return A_LESS_THAN_B;
-}
-#endif
-
Datum
btint8sortsupport(PG_FUNCTION_ARGS)
{
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
-#if SIZEOF_DATUM >= 8
ssup->comparator = ssup_datum_signed_cmp;
-#else
- ssup->comparator = btint8fastcmp;
-#endif
PG_RETURN_VOID();
}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 9a4de1616bc..e8909406686 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4390,7 +4390,7 @@ WriteControlFile(void)
ControlFile->toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
ControlFile->loblksize = LOBLKSIZE;
- ControlFile->float8ByVal = FLOAT8PASSBYVAL;
+ ControlFile->float8ByVal = true; /* vestigial */
/*
* Initialize the default 'char' signedness.
@@ -4651,23 +4651,7 @@ ReadControlFile(void)
"LOBLKSIZE", (int) LOBLKSIZE),
errhint("It looks like you need to recompile or initdb.")));
-#ifdef USE_FLOAT8_BYVAL
- if (ControlFile->float8ByVal != true)
- ereport(FATAL,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("database files are incompatible with server"),
- errdetail("The database cluster was initialized without USE_FLOAT8_BYVAL"
- " but the server was compiled with USE_FLOAT8_BYVAL."),
- errhint("It looks like you need to recompile or initdb.")));
-#else
- if (ControlFile->float8ByVal != false)
- ereport(FATAL,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("database files are incompatible with server"),
- errdetail("The database cluster was initialized with USE_FLOAT8_BYVAL"
- " but the server was compiled without USE_FLOAT8_BYVAL."),
- errhint("It looks like you need to recompile or initdb.")));
-#endif
+ Assert(ControlFile->float8ByVal); /* vestigial, not worth an error msg */
wal_segment_size = ControlFile->xlog_seg_size;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index df3231fcd41..6c02aee7267 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -1054,8 +1054,7 @@ sub morph_row_for_schemapg
}
# Expand booleans from 'f'/'t' to 'false'/'true'.
- # Some values might be other macros (eg FLOAT8PASSBYVAL),
- # don't change.
+ # Some values might be other macros, if so don't change.
elsif ($atttype eq 'bool')
{
$row->{$attname} = 'true' if $row->{$attname} eq 't';
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 1ec523ee3e5..3cd9b69edc5 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -285,8 +285,7 @@ TypeCreate(Oid newTypeOid,
errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
alignment, internalSize)));
}
-#if SIZEOF_DATUM == 8
- else if (internalSize == (int16) sizeof(Datum))
+ else if (internalSize == (int16) sizeof(int64))
{
if (alignment != TYPALIGN_DOUBLE)
ereport(ERROR,
@@ -294,7 +293,6 @@ TypeCreate(Oid newTypeOid,
errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
alignment, internalSize)));
}
-#endif
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 64605be3178..2ef0bb7f663 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -410,7 +410,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
parse->limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
sizeof(int64),
Int64GetDatum(1), false,
- FLOAT8PASSBYVAL);
+ true);
/*
* Generate the best paths for this query, telling query_planner that we
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5ba0d22befd..0d5a692e5fd 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -4915,7 +4915,7 @@ create_partial_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
sizeof(int64),
Int64GetDatum(1), false,
- FLOAT8PASSBYVAL);
+ true);
/*
* Apply a LimitPath onto the partial path to restrict the
@@ -5118,7 +5118,7 @@ create_final_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
sizeof(int64),
Int64GetDatum(1), false,
- FLOAT8PASSBYVAL);
+ true);
/*
* If the query already has a LIMIT clause, then we could
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index d6feb16aef3..203b7a32178 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -408,7 +408,7 @@ make_const(ParseState *pstate, A_Const *aconst)
typeid = INT8OID;
typelen = sizeof(int64);
- typebyval = FLOAT8PASSBYVAL; /* int8 and float8 alike */
+ typebyval = true;
}
}
else
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..6ac83ea1a82 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -69,7 +69,7 @@ static int nextSemaNumber; /* next free sem num in last sema set */
static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey,
- int numSems);
+ int numSems, bool retry_ok);
static void IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum,
int value);
static void IpcSemaphoreKill(IpcSemaphoreId semId);
@@ -88,9 +88,13 @@ static void ReleaseSemaphores(int status, Datum arg);
* If we fail with a failure code other than collision-with-existing-set,
* print out an error and abort. Other types of errors suggest nonrecoverable
* problems.
+ *
+ * Unfortunately, it's sometimes hard to tell whether errors are
+ * nonrecoverable. Our caller keeps track of whether continuing to retry
+ * is sane or not; if not, we abort on failure regardless of the errno.
*/
static IpcSemaphoreId
-InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
+InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems, bool retry_ok)
{
int semId;
@@ -101,16 +105,27 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
int saved_errno = errno;
/*
- * Fail quietly if error indicates a collision with existing set. One
- * would expect EEXIST, given that we said IPC_EXCL, but perhaps we
- * could get a permission violation instead? Also, EIDRM might occur
- * if an old set is slated for destruction but not gone yet.
+ * Fail quietly if error suggests a collision with an existing set and
+ * our caller has not lost patience.
+ *
+ * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
+ * we could get a permission violation instead. On some platforms
+ * EINVAL will be reported if the existing set has too few semaphores.
+ * Also, EIDRM might occur if an old set is slated for destruction but
+ * not gone yet.
+ *
+ * EINVAL is the key reason why we need the caller-level loop limit,
+ * as it can also mean that the platform's SEMMSL is less than
+ * numSems, and that condition can't be fixed by trying another key.
*/
- if (saved_errno == EEXIST || saved_errno == EACCES
+ if (retry_ok &&
+ (saved_errno == EEXIST
+ || saved_errno == EACCES
+ || saved_errno == EINVAL
#ifdef EIDRM
- || saved_errno == EIDRM
+ || saved_errno == EIDRM
#endif
- )
+ ))
return -1;
/*
@@ -207,17 +222,22 @@ IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum)
static IpcSemaphoreId
IpcSemaphoreCreate(int numSems)
{
+ int num_tries = 0;
IpcSemaphoreId semId;
union semun semun;
PGSemaphoreData mysema;
/* Loop till we find a free IPC key */
- for (nextSemaKey++;; nextSemaKey++)
+ for (nextSemaKey++;; nextSemaKey++, num_tries++)
{
pid_t creatorPID;
- /* Try to create new semaphore set */
- semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
+ /*
+ * Try to create new semaphore set. Give up after trying 1000
+ * distinct IPC keys.
+ */
+ semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1,
+ num_tries < 1000);
if (semId >= 0)
break; /* successful create */
@@ -254,7 +274,7 @@ IpcSemaphoreCreate(int numSems)
/*
* Now try again to create the sema set.
*/
- semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
+ semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1, true);
if (semId >= 0)
break; /* successful create */
diff --git a/src/backend/rewrite/rewriteSearchCycle.c b/src/backend/rewrite/rewriteSearchCycle.c
index 19b89dee0d0..9f95d4dc1b0 100644
--- a/src/backend/rewrite/rewriteSearchCycle.c
+++ b/src/backend/rewrite/rewriteSearchCycle.c
@@ -320,7 +320,7 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
if (cte->search_clause->search_breadth_first)
{
search_col_rowexpr->args = lcons(makeConst(INT8OID, -1, InvalidOid, sizeof(int64),
- Int64GetDatum(0), false, FLOAT8PASSBYVAL),
+ Int64GetDatum(0), false, true),
search_col_rowexpr->args);
search_col_rowexpr->colnames = lcons(makeString("*DEPTH*"), search_col_rowexpr->colnames);
texpr = (Expr *) search_col_rowexpr;
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..2704e80b3a7 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -399,7 +399,7 @@ cancel_before_shmem_exit(pg_on_exit_callback function, Datum arg)
before_shmem_exit_list[before_shmem_exit_index - 1].arg == arg)
--before_shmem_exit_index;
else
- elog(ERROR, "before_shmem_exit callback (%p,0x%" PRIxPTR ") is not the latest entry",
+ elog(ERROR, "before_shmem_exit callback (%p,0x%" PRIx64 ") is not the latest entry",
function, arg);
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c8f53c6fbe7..c833e7df1fd 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3406,7 +3406,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
case FLOAT8OID:
elmlen = sizeof(float8);
- elmbyval = FLOAT8PASSBYVAL;
+ elmbyval = true;
elmalign = TYPALIGN_DOUBLE;
break;
@@ -3424,7 +3424,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
case INT8OID:
elmlen = sizeof(int64);
- elmbyval = FLOAT8PASSBYVAL;
+ elmbyval = true;
elmalign = TYPALIGN_DOUBLE;
break;
@@ -3718,7 +3718,7 @@ deconstruct_array_builtin(ArrayType *array,
case FLOAT8OID:
elmlen = sizeof(float8);
- elmbyval = FLOAT8PASSBYVAL;
+ elmbyval = true;
elmalign = TYPALIGN_DOUBLE;
break;
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 9dd5889f34c..bdea490202a 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -718,76 +718,29 @@ int8lcm(PG_FUNCTION_ARGS)
Datum
int8inc(PG_FUNCTION_ARGS)
{
- /*
- * When int8 is pass-by-reference, we provide this special case to avoid
- * palloc overhead for COUNT(): when called as an aggregate, we know that
- * the argument is modifiable local storage, so just update it in-place.
- * (If int8 is pass-by-value, then of course this is useless as well as
- * incorrect, so just ifdef it out.)
- */
-#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
- if (AggCheckCallContext(fcinfo, NULL))
- {
- int64 *arg = (int64 *) PG_GETARG_POINTER(0);
-
- if (unlikely(pg_add_s64_overflow(*arg, 1, arg)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- PG_RETURN_POINTER(arg);
- }
- else
-#endif
- {
- /* Not called as an aggregate, so just do it the dumb way */
- int64 arg = PG_GETARG_INT64(0);
- int64 result;
+ int64 arg = PG_GETARG_INT64(0);
+ int64 result;
- if (unlikely(pg_add_s64_overflow(arg, 1, &result)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
+ if (unlikely(pg_add_s64_overflow(arg, 1, &result)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
- PG_RETURN_INT64(result);
- }
+ PG_RETURN_INT64(result);
}
Datum
int8dec(PG_FUNCTION_ARGS)
{
- /*
- * When int8 is pass-by-reference, we provide this special case to avoid
- * palloc overhead for COUNT(): when called as an aggregate, we know that
- * the argument is modifiable local storage, so just update it in-place.
- * (If int8 is pass-by-value, then of course this is useless as well as
- * incorrect, so just ifdef it out.)
- */
-#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
- if (AggCheckCallContext(fcinfo, NULL))
- {
- int64 *arg = (int64 *) PG_GETARG_POINTER(0);
-
- if (unlikely(pg_sub_s64_overflow(*arg, 1, arg)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
- PG_RETURN_POINTER(arg);
- }
- else
-#endif
- {
- /* Not called as an aggregate, so just do it the dumb way */
- int64 arg = PG_GETARG_INT64(0);
- int64 result;
+ int64 arg = PG_GETARG_INT64(0);
+ int64 result;
- if (unlikely(pg_sub_s64_overflow(arg, 1, &result)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
+ if (unlikely(pg_sub_s64_overflow(arg, 1, &result)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
- PG_RETURN_INT64(result);
- }
+ PG_RETURN_INT64(result);
}
diff --git a/src/backend/utils/adt/mac.c b/src/backend/utils/adt/mac.c
index 3644e9735f5..bb38ef2f5e4 100644
--- a/src/backend/utils/adt/mac.c
+++ b/src/backend/utils/adt/mac.c
@@ -481,33 +481,26 @@ macaddr_abbrev_convert(Datum original, SortSupport ssup)
Datum res;
/*
- * On a 64-bit machine, zero out the 8-byte datum and copy the 6 bytes of
- * the MAC address in. There will be two bytes of zero padding on the end
- * of the least significant bits.
+ * Zero out the 8-byte Datum and copy in the 6 bytes of the MAC address.
+ * There will be two bytes of zero padding on the end of the least
+ * significant bits.
*/
-#if SIZEOF_DATUM == 8
- memset(&res, 0, SIZEOF_DATUM);
+ StaticAssertStmt(sizeof(res) >= sizeof(macaddr),
+ "Datum is too small for macaddr");
+ memset(&res, 0, sizeof(res));
memcpy(&res, authoritative, sizeof(macaddr));
-#else /* SIZEOF_DATUM != 8 */
- memcpy(&res, authoritative, SIZEOF_DATUM);
-#endif
uss->input_count += 1;
/*
- * Cardinality estimation. The estimate uses uint32, so on a 64-bit
- * architecture, XOR the two 32-bit halves together to produce slightly
- * more entropy. The two zeroed bytes won't have any practical impact on
- * this operation.
+ * Cardinality estimation. The estimate uses uint32, so XOR the two 32-bit
+ * halves together to produce slightly more entropy. The two zeroed bytes
+ * won't have any practical impact on this operation.
*/
if (uss->estimating)
{
uint32 tmp;
-#if SIZEOF_DATUM == 8
- tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
-#else /* SIZEOF_DATUM != 8 */
- tmp = (uint32) res;
-#endif
+ tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32);
addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
}
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 9fd211b2d45..3cb0ab6829a 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -567,24 +567,11 @@ network_abbrev_abort(int memtupcount, SortSupport ssup)
*
* When generating abbreviated keys for SortSupport, we pack as much as we can
* into a datum while ensuring that when comparing those keys as integers,
- * these rules will be respected. Exact contents depend on IP family and datum
- * size.
+ * these rules will be respected. Exact contents depend on IP family:
*
* IPv4
* ----
*
- * 4 byte datums:
- *
- * Start with 1 bit for the IP family (IPv4 or IPv6; this bit is present in
- * every case below) followed by all but 1 of the netmasked bits.
- *
- * +----------+---------------------+
- * | 1 bit IP | 31 bits network | (1 bit network
- * | family | (truncated) | omitted)
- * +----------+---------------------+
- *
- * 8 byte datums:
- *
* We have space to store all netmasked bits, followed by the netmask size,
* followed by 25 bits of the subnet (25 bits is usually more than enough in
* practice). cidr datums always have all-zero subnet bits.
@@ -597,15 +584,6 @@ network_abbrev_abort(int memtupcount, SortSupport ssup)
* IPv6
* ----
*
- * 4 byte datums:
- *
- * +----------+---------------------+
- * | 1 bit IP | 31 bits network | (up to 97 bits
- * | family | (truncated) | network omitted)
- * +----------+---------------------+
- *
- * 8 byte datums:
- *
* +----------+---------------------------------+
* | 1 bit IP | 63 bits network | (up to 65 bits
* | family | (truncated) | network omitted)
@@ -628,8 +606,7 @@ network_abbrev_convert(Datum original, SortSupport ssup)
/*
* Get an unsigned integer representation of the IP address by taking its
* first 4 or 8 bytes. Always take all 4 bytes of an IPv4 address. Take
- * the first 8 bytes of an IPv6 address with an 8 byte datum and 4 bytes
- * otherwise.
+ * the first 8 bytes of an IPv6 address.
*
* We're consuming an array of unsigned char, so byteswap on little endian
* systems (an inet's ipaddr field stores the most significant byte
@@ -659,7 +636,7 @@ network_abbrev_convert(Datum original, SortSupport ssup)
ipaddr_datum = DatumBigEndianToNative(ipaddr_datum);
/* Initialize result with ipfamily (most significant) bit set */
- res = ((Datum) 1) << (SIZEOF_DATUM * BITS_PER_BYTE - 1);
+ res = ((Datum) 1) << (sizeof(Datum) * BITS_PER_BYTE - 1);
}
/*
@@ -668,8 +645,7 @@ network_abbrev_convert(Datum original, SortSupport ssup)
* while low order bits go in "subnet" component when there is space for
* one. This is often accomplished by generating a temp datum subnet
* bitmask, which we may reuse later when generating the subnet bits
- * themselves. (Note that subnet bits are only used with IPv4 datums on
- * platforms where datum is 8 bytes.)
+ * themselves.
*
* The number of bits in subnet is used to generate a datum subnet
* bitmask. For example, with a /24 IPv4 datum there are 8 subnet bits
@@ -681,14 +657,14 @@ network_abbrev_convert(Datum original, SortSupport ssup)
subnet_size = ip_maxbits(authoritative) - ip_bits(authoritative);
Assert(subnet_size >= 0);
/* subnet size must work with prefix ipaddr cases */
- subnet_size %= SIZEOF_DATUM * BITS_PER_BYTE;
+ subnet_size %= sizeof(Datum) * BITS_PER_BYTE;
if (ip_bits(authoritative) == 0)
{
/* Fit as many ipaddr bits as possible into subnet */
subnet_bitmask = ((Datum) 0) - 1;
network = 0;
}
- else if (ip_bits(authoritative) < SIZEOF_DATUM * BITS_PER_BYTE)
+ else if (ip_bits(authoritative) < sizeof(Datum) * BITS_PER_BYTE)
{
/* Split ipaddr bits between network and subnet */
subnet_bitmask = (((Datum) 1) << subnet_size) - 1;
@@ -701,12 +677,11 @@ network_abbrev_convert(Datum original, SortSupport ssup)
network = ipaddr_datum;
}
-#if SIZEOF_DATUM == 8
if (ip_family(authoritative) == PGSQL_AF_INET)
{
/*
- * IPv4 with 8 byte datums: keep all 32 netmasked bits, netmask size,
- * and most significant 25 subnet bits
+ * IPv4: keep all 32 netmasked bits, netmask size, and most
+ * significant 25 subnet bits
*/
Datum netmask_size = (Datum) ip_bits(authoritative);
Datum subnet;
@@ -750,12 +725,11 @@ network_abbrev_convert(Datum original, SortSupport ssup)
res |= network | netmask_size | subnet;
}
else
-#endif
{
/*
- * 4 byte datums, or IPv6 with 8 byte datums: Use as many of the
- * netmasked bits as will fit in final abbreviated key. Avoid
- * clobbering the ipfamily bit that was set earlier.
+ * IPv6: Use as many of the netmasked bits as will fit in final
+ * abbreviated key. Avoid clobbering the ipfamily bit that was set
+ * earlier.
*/
res |= network >> 1;
}
@@ -767,11 +741,7 @@ network_abbrev_convert(Datum original, SortSupport ssup)
{
uint32 tmp;
-#if SIZEOF_DATUM == 8
- tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
-#else /* SIZEOF_DATUM != 8 */
- tmp = (uint32) res;
-#endif
+ tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32);
addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
}
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 122f2efab8b..b6287f5d973 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -392,30 +392,21 @@ typedef struct NumericSumAccum
/*
* We define our own macros for packing and unpacking abbreviated-key
- * representations for numeric values in order to avoid depending on
- * USE_FLOAT8_BYVAL. The type of abbreviation we use is based only on
- * the size of a datum, not the argument-passing convention for float8.
+ * representations, just to have a notational indication that that's
+ * what we're doing. Now that sizeof(Datum) is always 8, we can rely
+ * on fitting an int64 into Datum.
*
- * The range of abbreviations for finite values is from +PG_INT64/32_MAX
- * to -PG_INT64/32_MAX. NaN has the abbreviation PG_INT64/32_MIN, and we
+ * The range of abbreviations for finite values is from +PG_INT64_MAX
+ * to -PG_INT64_MAX. NaN has the abbreviation PG_INT64_MIN, and we
* define the sort ordering to make that work out properly (see further
* comments below). PINF and NINF share the abbreviations of the largest
* and smallest finite abbreviation classes.
*/
-#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE)
-#if SIZEOF_DATUM == 8
-#define NumericAbbrevGetDatum(X) ((Datum) (X))
-#define DatumGetNumericAbbrev(X) ((int64) (X))
+#define NumericAbbrevGetDatum(X) Int64GetDatum(X)
+#define DatumGetNumericAbbrev(X) DatumGetInt64(X)
#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT64_MIN)
#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT64_MAX)
#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT64_MAX)
-#else
-#define NumericAbbrevGetDatum(X) ((Datum) (X))
-#define DatumGetNumericAbbrev(X) ((int32) (X))
-#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT32_MIN)
-#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT32_MAX)
-#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT32_MAX)
-#endif
/* ----------
@@ -2096,12 +2087,11 @@ compute_bucket(Numeric operand, Numeric bound1, Numeric bound2,
* while this could be worked on itself, the abbreviation strategy gives more
* speedup in many common cases.
*
- * Two different representations are used for the abbreviated form, one in
- * int32 and one in int64, whichever fits into a by-value Datum. In both cases
- * the representation is negated relative to the original value, because we use
- * the largest negative value for NaN, which sorts higher than other values. We
- * convert the absolute value of the numeric to a 31-bit or 63-bit positive
- * value, and then negate it if the original number was positive.
+ * The abbreviated format is an int64. The representation is negated relative
+ * to the original value, because we use the largest negative value for NaN,
+ * which sorts higher than other values. We convert the absolute value of the
+ * numeric to a 63-bit positive value, and then negate it if the original
+ * number was positive.
*
* We abort the abbreviation process if the abbreviation cardinality is below
* 0.01% of the row count (1 per 10k non-null rows). The actual break-even
@@ -2328,7 +2318,7 @@ numeric_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
}
/*
- * Abbreviate a NumericVar according to the available bit size.
+ * Abbreviate a NumericVar into the 64-bit sortsupport size.
*
* The 31-bit value is constructed as:
*
@@ -2372,9 +2362,6 @@ numeric_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
* with all bits zero. This allows simple comparisons to work on the composite
* value.
*/
-
-#if NUMERIC_ABBREV_BITS == 64
-
static Datum
numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss)
{
@@ -2426,84 +2413,6 @@ numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss)
return NumericAbbrevGetDatum(result);
}
-#endif /* NUMERIC_ABBREV_BITS == 64 */
-
-#if NUMERIC_ABBREV_BITS == 32
-
-static Datum
-numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss)
-{
- int ndigits = var->ndigits;
- int weight = var->weight;
- int32 result;
-
- if (ndigits == 0 || weight < -11)
- {
- result = 0;
- }
- else if (weight > 20)
- {
- result = PG_INT32_MAX;
- }
- else
- {
- NumericDigit nxt1 = (ndigits > 1) ? var->digits[1] : 0;
-
- weight = (weight + 11) * 4;
-
- result = var->digits[0];
-
- /*
- * "result" now has 1 to 4 nonzero decimal digits. We pack in more
- * digits to make 7 in total (largest we can fit in 24 bits)
- */
-
- if (result > 999)
- {
- /* already have 4 digits, add 3 more */
- result = (result * 1000) + (nxt1 / 10);
- weight += 3;
- }
- else if (result > 99)
- {
- /* already have 3 digits, add 4 more */
- result = (result * 10000) + nxt1;
- weight += 2;
- }
- else if (result > 9)
- {
- NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0;
-
- /* already have 2 digits, add 5 more */
- result = (result * 100000) + (nxt1 * 10) + (nxt2 / 1000);
- weight += 1;
- }
- else
- {
- NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0;
-
- /* already have 1 digit, add 6 more */
- result = (result * 1000000) + (nxt1 * 100) + (nxt2 / 100);
- }
-
- result = result | (weight << 24);
- }
-
- /* the abbrev is negated relative to the original */
- if (var->sign == NUMERIC_POS)
- result = -result;
-
- if (nss->estimating)
- {
- uint32 tmp = (uint32) result;
-
- addHyperLogLog(&nss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
- }
-
- return NumericAbbrevGetDatum(result);
-}
-
-#endif /* NUMERIC_ABBREV_BITS == 32 */
/*
* Ordinary (non-sortsupport) comparisons follow.
@@ -6453,6 +6362,7 @@ numeric_poly_stddev_pop(PG_FUNCTION_ARGS)
Datum
int2_sum(PG_FUNCTION_ARGS)
{
+ int64 oldsum;
int64 newval;
if (PG_ARGISNULL(0))
@@ -6465,43 +6375,22 @@ int2_sum(PG_FUNCTION_ARGS)
PG_RETURN_INT64(newval);
}
- /*
- * If we're invoked as an aggregate, we can cheat and modify our first
- * parameter in-place to avoid palloc overhead. If not, we need to return
- * the new value of the transition variable. (If int8 is pass-by-value,
- * then of course this is useless as well as incorrect, so just ifdef it
- * out.)
- */
-#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
- if (AggCheckCallContext(fcinfo, NULL))
- {
- int64 *oldsum = (int64 *) PG_GETARG_POINTER(0);
-
- /* Leave the running sum unchanged in the new input is null */
- if (!PG_ARGISNULL(1))
- *oldsum = *oldsum + (int64) PG_GETARG_INT16(1);
+ oldsum = PG_GETARG_INT64(0);
- PG_RETURN_POINTER(oldsum);
- }
- else
-#endif
- {
- int64 oldsum = PG_GETARG_INT64(0);
-
- /* Leave sum unchanged if new input is null. */
- if (PG_ARGISNULL(1))
- PG_RETURN_INT64(oldsum);
+ /* Leave sum unchanged if new input is null. */
+ if (PG_ARGISNULL(1))
+ PG_RETURN_INT64(oldsum);
- /* OK to do the addition. */
- newval = oldsum + (int64) PG_GETARG_INT16(1);
+ /* OK to do the addition. */
+ newval = oldsum + (int64) PG_GETARG_INT16(1);
- PG_RETURN_INT64(newval);
- }
+ PG_RETURN_INT64(newval);
}
Datum
int4_sum(PG_FUNCTION_ARGS)
{
+ int64 oldsum;
int64 newval;
if (PG_ARGISNULL(0))
@@ -6514,38 +6403,16 @@ int4_sum(PG_FUNCTION_ARGS)
PG_RETURN_INT64(newval);
}
- /*
- * If we're invoked as an aggregate, we can cheat and modify our first
- * parameter in-place to avoid palloc overhead. If not, we need to return
- * the new value of the transition variable. (If int8 is pass-by-value,
- * then of course this is useless as well as incorrect, so just ifdef it
- * out.)
- */
-#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
- if (AggCheckCallContext(fcinfo, NULL))
- {
- int64 *oldsum = (int64 *) PG_GETARG_POINTER(0);
+ oldsum = PG_GETARG_INT64(0);
- /* Leave the running sum unchanged in the new input is null */
- if (!PG_ARGISNULL(1))
- *oldsum = *oldsum + (int64) PG_GETARG_INT32(1);
-
- PG_RETURN_POINTER(oldsum);
- }
- else
-#endif
- {
- int64 oldsum = PG_GETARG_INT64(0);
-
- /* Leave sum unchanged if new input is null. */
- if (PG_ARGISNULL(1))
- PG_RETURN_INT64(oldsum);
+ /* Leave sum unchanged if new input is null. */
+ if (PG_ARGISNULL(1))
+ PG_RETURN_INT64(oldsum);
- /* OK to do the addition. */
- newval = oldsum + (int64) PG_GETARG_INT32(1);
+ /* OK to do the addition. */
+ newval = oldsum + (int64) PG_GETARG_INT32(1);
- PG_RETURN_INT64(newval);
- }
+ PG_RETURN_INT64(newval);
}
/*
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 9457d239715..c41b191be62 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -1007,7 +1007,7 @@ percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
FLOAT8OID,
/* hard-wired info on type float8 */
sizeof(float8),
- FLOAT8PASSBYVAL,
+ true,
TYPALIGN_DOUBLE,
float8_lerp);
}
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index a18196d8a34..36e885af2dd 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -397,7 +397,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
stats->numvalues[slot_idx] = num_hist;
stats->statypid[slot_idx] = FLOAT8OID;
stats->statyplen[slot_idx] = sizeof(float8);
- stats->statypbyval[slot_idx] = FLOAT8PASSBYVAL;
+ stats->statypbyval[slot_idx] = true;
stats->statypalign[slot_idx] = 'd';
/* Store the fraction of empty ranges */
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e640b48205b..3e5f9dc1458 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2275,33 +2275,12 @@ timestamp_cmp(PG_FUNCTION_ARGS)
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}
-#if SIZEOF_DATUM < 8
-/* note: this is used for timestamptz also */
-static int
-timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
- Timestamp a = DatumGetTimestamp(x);
- Timestamp b = DatumGetTimestamp(y);
-
- return timestamp_cmp_internal(a, b);
-}
-#endif
-
Datum
timestamp_sortsupport(PG_FUNCTION_ARGS)
{
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
-#if SIZEOF_DATUM >= 8
-
- /*
- * If this build has pass-by-value timestamps, then we can use a standard
- * comparator function.
- */
ssup->comparator = ssup_datum_signed_cmp;
-#else
- ssup->comparator = timestamp_fastcmp;
-#endif
PG_RETURN_VOID();
}
diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index bce7309c183..7413239f7af 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -398,11 +398,7 @@ uuid_abbrev_convert(Datum original, SortSupport ssup)
{
uint32 tmp;
-#if SIZEOF_DATUM == 8
- tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
-#else /* SIZEOF_DATUM != 8 */
- tmp = (uint32) res;
-#endif
+ tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32);
addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 11b442a5941..2c398cd9e5c 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -1671,14 +1671,13 @@ varstr_sortsupport(SortSupport ssup, Oid typid, Oid collid)
*
* Even apart from the risk of broken locales, it's possible that
* there are platforms where the use of abbreviated keys should be
- * disabled at compile time. Having only 4 byte datums could make
- * worst-case performance drastically more likely, for example.
- * Moreover, macOS's strxfrm() implementation is known to not
- * effectively concentrate a significant amount of entropy from the
- * original string in earlier transformed blobs. It's possible that
- * other supported platforms are similarly encumbered. So, if we ever
- * get past disabling this categorically, we may still want or need to
- * disable it for particular platforms.
+ * disabled at compile time. For example, macOS's strxfrm()
+ * implementation is known to not effectively concentrate a
+ * significant amount of entropy from the original string in earlier
+ * transformed blobs. It's possible that other supported platforms
+ * are similarly encumbered. So, if we ever get past disabling this
+ * categorically, we may still want or need to disable it for
+ * particular platforms.
*/
if (!pg_strxfrm_enabled(locale))
abbreviate = false;
@@ -2132,18 +2131,12 @@ varstr_abbrev_convert(Datum original, SortSupport ssup)
addHyperLogLog(&sss->full_card, hash);
/* Hash abbreviated key */
-#if SIZEOF_DATUM == 8
{
- uint32 lohalf,
- hihalf;
+ uint32 tmp;
- lohalf = (uint32) res;
- hihalf = (uint32) (res >> 32);
- hash = DatumGetUInt32(hash_uint32(lohalf ^ hihalf));
+ tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32);
+ hash = DatumGetUInt32(hash_uint32(tmp));
}
-#else /* SIZEOF_DATUM != 8 */
- hash = DatumGetUInt32(hash_uint32((uint32) res));
-#endif
addHyperLogLog(&sss->abbr_card, hash);
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 782291d9998..5543440a33e 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1789,41 +1789,6 @@ OidSendFunctionCall(Oid functionId, Datum val)
/*-------------------------------------------------------------------------
- * Support routines for standard maybe-pass-by-reference datatypes
- *
- * int8 and float8 can be passed by value if Datum is wide enough.
- * (For backwards-compatibility reasons, we allow pass-by-ref to be chosen
- * at compile time even if pass-by-val is possible.)
- *
- * Note: there is only one switch controlling the pass-by-value option for
- * both int8 and float8; this is to avoid making things unduly complicated
- * for the timestamp types, which might have either representation.
- *-------------------------------------------------------------------------
- */
-
-#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
-
-Datum
-Int64GetDatum(int64 X)
-{
- int64 *retval = (int64 *) palloc(sizeof(int64));
-
- *retval = X;
- return PointerGetDatum(retval);
-}
-
-Datum
-Float8GetDatum(float8 X)
-{
- float8 *retval = (float8 *) palloc(sizeof(float8));
-
- *retval = X;
- return PointerGetDatum(retval);
-}
-#endif /* USE_FLOAT8_BYVAL */
-
-
-/*-------------------------------------------------------------------------
* Support routines for toastable datatypes
*-------------------------------------------------------------------------
*/
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index d39f3e1b655..fca84ded6dd 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -231,11 +231,8 @@ hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
* 'kind' into the hash. Just add it with hash_combine(), it perturbs the
* result enough for our purposes.
*/
-#if SIZEOF_DATUM == 8
- return hash_combine64(murmurhash64((uint64) value), (uint64) kind);
-#else
- return hash_combine(murmurhash32((uint32) value), (uint32) kind);
-#endif
+ return hash_combine64(murmurhash64((uint64) value),
+ (uint64) (uintptr_t) kind);
}
/*
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 65ab83fff8b..5d4411dc33f 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -512,7 +512,6 @@ qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
return state->base.comparetup_tiebreak(a, b, state);
}
-#if SIZEOF_DATUM >= 8
/* Used if first key's comparator is ssup_datum_signed_cmp */
static pg_attribute_always_inline int
qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
@@ -535,7 +534,6 @@ qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
return state->base.comparetup_tiebreak(a, b, state);
}
-#endif
/* Used if first key's comparator is ssup_datum_int32_cmp */
static pg_attribute_always_inline int
@@ -578,7 +576,6 @@ qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
#define ST_DEFINE
#include "lib/sort_template.h"
-#if SIZEOF_DATUM >= 8
#define ST_SORT qsort_tuple_signed
#define ST_ELEMENT_TYPE SortTuple
#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
@@ -587,7 +584,6 @@ qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
#define ST_SCOPE static
#define ST_DEFINE
#include "lib/sort_template.h"
-#endif
#define ST_SORT qsort_tuple_int32
#define ST_ELEMENT_TYPE SortTuple
@@ -2692,7 +2688,6 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
state);
return;
}
-#if SIZEOF_DATUM >= 8
else if (state->base.sortKeys[0].comparator == ssup_datum_signed_cmp)
{
qsort_tuple_signed(state->memtuples,
@@ -2700,7 +2695,6 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
state);
return;
}
-#endif
else if (state->base.sortKeys[0].comparator == ssup_datum_int32_cmp)
{
qsort_tuple_int32(state->memtuples,
@@ -3146,7 +3140,6 @@ ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup)
return 0;
}
-#if SIZEOF_DATUM >= 8
int
ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
{
@@ -3160,7 +3153,6 @@ ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
else
return 0;
}
-#endif
int
ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 62bbd08d9f6..92fe2f531f7 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1580,9 +1580,6 @@ bootstrap_template1(void)
bki_lines = replace_token(bki_lines, "ALIGNOF_POINTER",
(sizeof(Pointer) == 4) ? "i" : "d");
- bki_lines = replace_token(bki_lines, "FLOAT8PASSBYVAL",
- FLOAT8PASSBYVAL ? "true" : "false");
-
bki_lines = replace_token(bki_lines, "POSTGRES",
escape_quotes_bki(username));
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index e876f35f38e..7a4e4eb9570 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -719,7 +719,7 @@ GuessControlValues(void)
ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
ControlFile.loblksize = LOBLKSIZE;
- ControlFile.float8ByVal = FLOAT8PASSBYVAL;
+ ControlFile.float8ByVal = true; /* vestigial */
/*
* XXX eventually, should try to grovel through old XLOG to develop more
diff --git a/src/include/access/gin_tuple.h b/src/include/access/gin_tuple.h
index 702f7d12889..b4f103dec9a 100644
--- a/src/include/access/gin_tuple.h
+++ b/src/include/access/gin_tuple.h
@@ -15,7 +15,9 @@
#include "utils/sortsupport.h"
/*
- * Data for one key in a GIN index.
+ * Data for one key in a GIN index. (This is not the permanent in-index
+ * representation, but just a convenient format to use during the tuplesort
+ * stage of building a new GIN index.)
*/
typedef struct GinTuple
{
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index cb43a278f46..56ac64f0597 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -285,10 +285,12 @@ typedef struct SpGistCache
* If the prefix datum is of a pass-by-value type, it is stored in its
* Datum representation, that is its on-disk representation is of length
* sizeof(Datum). This is a fairly unfortunate choice, because in no other
- * place does Postgres use Datum as an on-disk representation; it creates
- * an unnecessary incompatibility between 32-bit and 64-bit builds. But the
- * compatibility loss is mostly theoretical since MAXIMUM_ALIGNOF typically
- * differs between such builds, too. Anyway we're stuck with it now.
+ * place does Postgres use Datum as an on-disk representation. Formerly it
+ * meant an unnecessary incompatibility between 32-bit and 64-bit builds, and
+ * as of v19 it instead creates a hazard for binary upgrades on 32-bit builds.
+ * Fortunately, that hazard seems mostly theoretical for lack of affected
+ * opclasses. Going forward, we will be using a fixed size of Datum so that
+ * there's no longer any pressing reason to change this.
*/
typedef struct SpGistInnerTupleData
{
@@ -377,8 +379,8 @@ typedef SpGistNodeTupleData *SpGistNodeTuple;
*
* size must be a multiple of MAXALIGN; also, it must be at least SGDTSIZE
* so that the tuple can be converted to REDIRECT status later. (This
- * restriction only adds bytes for a NULL leaf datum stored on a 32-bit
- * machine; otherwise alignment restrictions force it anyway.)
+ * restriction only adds bytes for a NULL leaf datum; otherwise alignment
+ * restrictions force it anyway.)
*/
typedef struct SpGistLeafTupleData
{
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 6240ec930e7..84b3e7fd896 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -39,9 +39,6 @@ att_isnull(int ATT, const bits8 *BITS)
* return the correct number of bytes fetched from the data area and extended
* to Datum form.
*
- * On machines where Datum is 8 bytes, we support fetching 8-byte byval
- * attributes; otherwise, only 1, 2, and 4-byte values are supported.
- *
* Note that T must already be properly aligned for this to work correctly.
*/
#define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)
@@ -62,10 +59,8 @@ fetch_att(const void *T, bool attbyval, int attlen)
return Int16GetDatum(*((const int16 *) T));
case sizeof(int32):
return Int32GetDatum(*((const int32 *) T));
-#if SIZEOF_DATUM == 8
- case sizeof(Datum):
- return *((const Datum *) T);
-#endif
+ case sizeof(int64):
+ return Int64GetDatum(*((const int64 *) T));
default:
elog(ERROR, "unsupported byval length: %d", attlen);
return 0;
@@ -221,11 +216,9 @@ store_att_byval(void *T, Datum newdatum, int attlen)
case sizeof(int32):
*(int32 *) T = DatumGetInt32(newdatum);
break;
-#if SIZEOF_DATUM == 8
- case sizeof(Datum):
- *(Datum *) T = newdatum;
+ case sizeof(int64):
+ *(int64 *) T = DatumGetInt64(newdatum);
break;
-#endif
default:
elog(ERROR, "unsupported byval length: %d", attlen);
}
diff --git a/src/include/c.h b/src/include/c.h
index bbdaa88c63a..39022f8a9dd 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -609,11 +609,11 @@ typedef signed int Offset;
typedef float float4;
typedef double float8;
-#ifdef USE_FLOAT8_BYVAL
+/*
+ * float8, int8, and related datatypes are now always pass-by-value.
+ * We keep this symbol to avoid breaking extension code that may use it.
+ */
#define FLOAT8PASSBYVAL true
-#else
-#define FLOAT8PASSBYVAL false
-#endif
/*
* Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId,
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index c4fe8b991af..0ca415b4261 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202508051
+#define CATALOG_VERSION_NO 202508131
#endif
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 29e4ffffc98..cb730aeac86 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -54,7 +54,7 @@
typcollation => 'C' },
{ oid => '20', array_type_oid => '1016',
descr => '~18 digit integer, 8-byte storage',
- typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'int8', typlen => '8', typbyval => 't',
typcategory => 'N', typinput => 'int8in', typoutput => 'int8out',
typreceive => 'int8recv', typsend => 'int8send', typalign => 'd' },
{ oid => '21', array_type_oid => '1005',
@@ -172,7 +172,7 @@
typoutput => 'pg_ddl_command_out', typreceive => 'pg_ddl_command_recv',
typsend => 'pg_ddl_command_send', typalign => 'ALIGNOF_POINTER' },
{ oid => '5069', array_type_oid => '271', descr => 'full transaction id',
- typname => 'xid8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'xid8', typlen => '8', typbyval => 't',
typcategory => 'U', typinput => 'xid8in', typoutput => 'xid8out',
typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'd' },
@@ -222,7 +222,7 @@
typsend => 'float4send', typalign => 'i' },
{ oid => '701', array_type_oid => '1022',
descr => 'double-precision floating point number, 8-byte storage',
- typname => 'float8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'float8', typlen => '8', typbyval => 't',
typcategory => 'N', typispreferred => 't', typinput => 'float8in',
typoutput => 'float8out', typreceive => 'float8recv', typsend => 'float8send',
typalign => 'd' },
@@ -237,7 +237,7 @@
typreceive => 'circle_recv', typsend => 'circle_send', typalign => 'd' },
{ oid => '790', array_type_oid => '791',
descr => 'monetary amounts, $d,ddd.cc',
- typname => 'money', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'money', typlen => '8', typbyval => 't',
typcategory => 'N', typinput => 'cash_in', typoutput => 'cash_out',
typreceive => 'cash_recv', typsend => 'cash_send', typalign => 'd' },
@@ -290,7 +290,7 @@
typinput => 'date_in', typoutput => 'date_out', typreceive => 'date_recv',
typsend => 'date_send', typalign => 'i' },
{ oid => '1083', array_type_oid => '1183', descr => 'time of day',
- typname => 'time', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ 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' },
@@ -298,14 +298,14 @@
# OIDS 1100 - 1199
{ oid => '1114', array_type_oid => '1115', descr => 'date and time',
- typname => 'timestamp', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'timestamp', typlen => '8', typbyval => 't',
typcategory => 'D', typinput => 'timestamp_in', typoutput => 'timestamp_out',
typreceive => 'timestamp_recv', typsend => 'timestamp_send',
typmodin => 'timestamptypmodin', typmodout => 'timestamptypmodout',
typalign => 'd' },
{ oid => '1184', array_type_oid => '1185',
descr => 'date and time with time zone',
- typname => 'timestamptz', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ typname => 'timestamptz', typlen => '8', typbyval => 't',
typcategory => 'D', typispreferred => 't', typinput => 'timestamptz_in',
typoutput => 'timestamptz_out', typreceive => 'timestamptz_recv',
typsend => 'timestamptz_send', typmodin => 'timestamptztypmodin',
@@ -413,7 +413,7 @@
# pg_lsn
{ oid => '3220', array_type_oid => '3221', descr => 'PostgreSQL LSN',
- typname => 'pg_lsn', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+ 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' },
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 0fe7b4ebc77..c7236e42972 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -469,7 +469,7 @@ typedef struct
int funcmaxargs; /* FUNC_MAX_ARGS */
int indexmaxkeys; /* INDEX_MAX_KEYS */
int namedatalen; /* NAMEDATALEN */
- int float8byval; /* FLOAT8PASSBYVAL */
+ int float8byval; /* FLOAT8PASSBYVAL (now vestigial) */
char abi_extra[32]; /* see pg_config_manual.h */
} Pg_abi_values;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fbe333d88fa..b2dc380b57b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -188,6 +188,8 @@ castNodeImpl(NodeTag type, void *ptr)
* ----------------------------------------------------------------
*/
+#ifndef FRONTEND
+
/*
* nodes/{outfuncs.c,print.c}
*/
@@ -198,7 +200,7 @@ extern void outNode(struct StringInfoData *str, const void *obj);
extern void outToken(struct StringInfoData *str, const char *s);
extern void outBitmapset(struct StringInfoData *str,
const struct Bitmapset *bms);
-extern void outDatum(struct StringInfoData *str, uintptr_t value,
+extern void outDatum(struct StringInfoData *str, Datum value,
int typlen, bool typbyval);
extern char *nodeToString(const void *obj);
extern char *nodeToStringWithLocations(const void *obj);
@@ -212,7 +214,7 @@ extern void *stringToNode(const char *str);
extern void *stringToNodeWithLocations(const char *str);
#endif
extern struct Bitmapset *readBitmapset(void);
-extern uintptr_t readDatum(bool typbyval);
+extern Datum readDatum(bool typbyval);
extern bool *readBoolCols(int numCols);
extern int *readIntCols(int numCols);
extern Oid *readOidCols(int numCols);
@@ -235,6 +237,8 @@ extern void *copyObjectImpl(const void *from);
*/
extern bool equal(const void *a, const void *b);
+#endif /* !FRONTEND */
+
/*
* Typedef for parse location. This is just an int, but this way
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 125d3eb5fff..7e1aa422332 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -74,17 +74,12 @@
#define PARTITION_MAX_KEYS 32
/*
- * Decide whether built-in 8-byte types, including float8, int8, and
- * timestamp, are passed by value. This is on by default if sizeof(Datum) >=
- * 8 (that is, on 64-bit platforms). If sizeof(Datum) < 8 (32-bit platforms),
- * this must be off. We keep this here as an option so that it is easy to
- * test the pass-by-reference code paths on 64-bit platforms.
- *
- * Changing this requires an initdb.
+ * This symbol is now vestigial: built-in 8-byte types, including float8,
+ * int8, and timestamp, are always passed by value since we require Datum
+ * to be wide enough to permit that. We continue to define the symbol here
+ * so as not to unnecessarily break extension code.
*/
-#if SIZEOF_VOID_P >= 8
#define USE_FLOAT8_BYVAL 1
-#endif
/*
diff --git a/src/include/port/pg_bswap.h b/src/include/port/pg_bswap.h
index 33648433c63..b15f6f6ac38 100644
--- a/src/include/port/pg_bswap.h
+++ b/src/include/port/pg_bswap.h
@@ -130,8 +130,7 @@ pg_bswap64(uint64 x)
/*
* Rearrange the bytes of a Datum from big-endian order into the native byte
- * order. On big-endian machines, this does nothing at all. Note that the C
- * type Datum is an unsigned integer type on all platforms.
+ * order. On big-endian machines, this does nothing at all.
*
* One possible application of the DatumBigEndianToNative() macro is to make
* bitwise comparisons cheaper. A simple 3-way comparison of Datums
@@ -139,23 +138,11 @@ pg_bswap64(uint64 x)
* the same result as a memcmp() of the corresponding original Datums, but can
* be much cheaper. It's generally safe to do this on big-endian systems
* without any special transformation occurring first.
- *
- * If SIZEOF_DATUM is not defined, then postgres.h wasn't included and these
- * macros probably shouldn't be used, so we define nothing. Note that
- * SIZEOF_DATUM == 8 would evaluate as 0 == 8 in that case, potentially
- * leading to the wrong implementation being selected and confusing errors, so
- * defining nothing is safest.
*/
-#ifdef SIZEOF_DATUM
#ifdef WORDS_BIGENDIAN
#define DatumBigEndianToNative(x) (x)
#else /* !WORDS_BIGENDIAN */
-#if SIZEOF_DATUM == 8
-#define DatumBigEndianToNative(x) pg_bswap64(x)
-#else /* SIZEOF_DATUM != 8 */
-#define DatumBigEndianToNative(x) pg_bswap32(x)
-#endif /* SIZEOF_DATUM == 8 */
+#define DatumBigEndianToNative(x) UInt64GetDatum(pg_bswap64(DatumGetUInt64(x)))
#endif /* WORDS_BIGENDIAN */
-#endif /* SIZEOF_DATUM */
#endif /* PG_BSWAP_H */
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a41a668687..357cbd6fd96 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -58,15 +58,22 @@
/*
* A Datum contains either a value of a pass-by-value type or a pointer to a
- * value of a pass-by-reference type. Therefore, we require:
- *
- * sizeof(Datum) == sizeof(void *) == 4 or 8
+ * value of a pass-by-reference type. Therefore, we must have
+ * sizeof(Datum) >= sizeof(void *). No current or foreseeable Postgres
+ * platform has pointers wider than 8 bytes, and standardizing on Datum being
+ * exactly 8 bytes has advantages in reducing cross-platform differences.
*
* The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
-typedef uintptr_t Datum;
+typedef uint64_t Datum;
+
+/*
+ * This symbol is now vestigial, but we continue to define it so as not to
+ * unnecessarily break extension code.
+ */
+#define SIZEOF_DATUM 8
/*
* A NullableDatum is used in places where both a Datum and its nullness needs
@@ -83,8 +90,6 @@ typedef struct NullableDatum
/* due to alignment padding this could be used for flags for free */
} NullableDatum;
-#define SIZEOF_DATUM SIZEOF_VOID_P
-
/*
* DatumGetBool
* Returns boolean value of a datum.
@@ -316,7 +321,7 @@ CommandIdGetDatum(CommandId X)
static inline Pointer
DatumGetPointer(Datum X)
{
- return (Pointer) X;
+ return (Pointer) (uintptr_t) X;
}
/*
@@ -326,7 +331,7 @@ DatumGetPointer(Datum X)
static inline Datum
PointerGetDatum(const void *X)
{
- return (Datum) X;
+ return (Datum) (uintptr_t) X;
}
/*
@@ -383,68 +388,41 @@ NameGetDatum(const NameData *X)
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
- *
- * Note: this function hides whether int64 is pass by value or by reference.
*/
static inline int64
DatumGetInt64(Datum X)
{
-#ifdef USE_FLOAT8_BYVAL
return (int64) X;
-#else
- return *((int64 *) DatumGetPointer(X));
-#endif
}
/*
* Int64GetDatum
* Returns datum representation for a 64-bit integer.
- *
- * Note: if int64 is pass by reference, this function returns a reference
- * to palloc'd space.
*/
-#ifdef USE_FLOAT8_BYVAL
static inline Datum
Int64GetDatum(int64 X)
{
return (Datum) X;
}
-#else
-extern Datum Int64GetDatum(int64 X);
-#endif
-
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
- *
- * Note: this function hides whether int64 is pass by value or by reference.
*/
static inline uint64
DatumGetUInt64(Datum X)
{
-#ifdef USE_FLOAT8_BYVAL
return (uint64) X;
-#else
- return *((uint64 *) DatumGetPointer(X));
-#endif
}
/*
* UInt64GetDatum
* Returns datum representation for a 64-bit unsigned integer.
- *
- * Note: if int64 is pass by reference, this function returns a reference
- * to palloc'd space.
*/
static inline Datum
UInt64GetDatum(uint64 X)
{
-#ifdef USE_FLOAT8_BYVAL
return (Datum) X;
-#else
- return Int64GetDatum((int64) X);
-#endif
}
/*
@@ -492,13 +470,10 @@ Float4GetDatum(float4 X)
/*
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
- *
- * Note: this function hides whether float8 is pass by value or by reference.
*/
static inline float8
DatumGetFloat8(Datum X)
{
-#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -507,19 +482,12 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-#else
- return *((float8 *) DatumGetPointer(X));
-#endif
}
/*
* Float8GetDatum
* Returns datum representation for an 8-byte floating point number.
- *
- * Note: if float8 is pass by reference, this function returns a reference
- * to palloc'd space.
*/
-#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
{
@@ -532,35 +500,22 @@ Float8GetDatum(float8 X)
myunion.value = X;
return Int64GetDatum(myunion.retval);
}
-#else
-extern Datum Float8GetDatum(float8 X);
-#endif
-
/*
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These macros were intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
- * sacrificing performance when they are. The argument must be a variable
- * that will exist and have the same value for as long as the Datum is needed.
- * In the pass-by-ref case, the address of the variable is taken to use as
- * the Datum. In the pass-by-val case, these are the same as the non-Fast
- * functions, except for asserting that the variable is of the correct type.
+ * sacrificing performance when they are. They are no longer different
+ * from the regular functions, though we keep the assertions to protect
+ * code that might get back-patched into older branches.
*/
-#ifdef USE_FLOAT8_BYVAL
#define Int64GetDatumFast(X) \
(AssertVariableIsOfTypeMacro(X, int64), Int64GetDatum(X))
#define Float8GetDatumFast(X) \
(AssertVariableIsOfTypeMacro(X, double), Float8GetDatum(X))
-#else
-#define Int64GetDatumFast(X) \
- (AssertVariableIsOfTypeMacro(X, int64), PointerGetDatum(&(X)))
-#define Float8GetDatumFast(X) \
- (AssertVariableIsOfTypeMacro(X, double), PointerGetDatum(&(X)))
-#endif
/* ----------------------------------------------------------------
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
index b7abaf7802d..c64527e2ee9 100644
--- a/src/include/utils/sortsupport.h
+++ b/src/include/utils/sortsupport.h
@@ -262,7 +262,6 @@ ApplyUnsignedSortComparator(Datum datum1, bool isNull1,
return compare;
}
-#if SIZEOF_DATUM >= 8
static inline int
ApplySignedSortComparator(Datum datum1, bool isNull1,
Datum datum2, bool isNull2,
@@ -296,7 +295,6 @@ ApplySignedSortComparator(Datum datum1, bool isNull1,
return compare;
}
-#endif
static inline int
ApplyInt32SortComparator(Datum datum1, bool isNull1,
@@ -376,9 +374,7 @@ ApplySortAbbrevFullComparator(Datum datum1, bool isNull1,
* are eligible for faster sorting.
*/
extern int ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup);
-#if SIZEOF_DATUM >= 8
extern int ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup);
-#endif
extern int ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup);
/* Other functions in utils/sort/sortsupport.c */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index afa85d9fca9..a3d12931fff 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -5494,6 +5494,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
*entry;
struct berval **values;
LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0};
+ int ldapversion = LDAP_VERSION3;
if ((url = strdup(purl)) == NULL)
{
@@ -5625,6 +5626,15 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
return 3;
}
+ if ((rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
+ {
+ libpq_append_error(errorMessage, "could not set LDAP protocol version: %s",
+ ldap_err2string(rc));
+ free(url);
+ ldap_unbind(ld);
+ return 3;
+ }
+
/*
* Perform an explicit anonymous bind.
*
diff --git a/src/test/isolation/expected/index-killtuples.out b/src/test/isolation/expected/index-killtuples.out
new file mode 100644
index 00000000000..be7ddd756ef
--- /dev/null
+++ b/src/test/isolation/expected/index-killtuples.out
@@ -0,0 +1,355 @@
+Parsed test spec with 1 sessions
+
+starting permutation: create_table fill_500 create_btree flush disable_seq disable_bitmap measure access flush result measure access flush result delete flush measure access flush result measure access flush result drop_table
+step create_table: CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null);
+step fill_500: INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 500) g(i);
+step create_btree: CREATE INDEX kill_prior_tuple_btree ON kill_prior_tuple USING btree (key);
+step flush: SELECT FROM pg_stat_force_next_flush();
+step disable_seq: SET enable_seqscan = false;
+step disable_bitmap: SET enable_bitmapscan = false;
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_btree on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_btree on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step delete: DELETE FROM kill_prior_tuple;
+step flush: SELECT FROM pg_stat_force_next_flush();
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_btree on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_btree on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 0
+(1 row)
+
+step drop_table: DROP TABLE IF EXISTS kill_prior_tuple;
+
+starting permutation: create_table fill_500 create_ext_btree_gist create_gist flush disable_seq disable_bitmap measure access flush result measure access flush result delete flush measure access flush result measure access flush result drop_table drop_ext_btree_gist
+step create_table: CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null);
+step fill_500: INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 500) g(i);
+step create_ext_btree_gist: CREATE EXTENSION btree_gist;
+step create_gist: CREATE INDEX kill_prior_tuple_gist ON kill_prior_tuple USING gist (key);
+step flush: SELECT FROM pg_stat_force_next_flush();
+step disable_seq: SET enable_seqscan = false;
+step disable_bitmap: SET enable_bitmapscan = false;
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step delete: DELETE FROM kill_prior_tuple;
+step flush: SELECT FROM pg_stat_force_next_flush();
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 0
+(1 row)
+
+step drop_table: DROP TABLE IF EXISTS kill_prior_tuple;
+step drop_ext_btree_gist: DROP EXTENSION btree_gist;
+
+starting permutation: create_table fill_10 create_ext_btree_gist create_gist flush disable_seq disable_bitmap measure access flush result measure access flush result delete flush measure access flush result measure access flush result drop_table drop_ext_btree_gist
+step create_table: CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null);
+step fill_10: INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 10) g(i);
+step create_ext_btree_gist: CREATE EXTENSION btree_gist;
+step create_gist: CREATE INDEX kill_prior_tuple_gist ON kill_prior_tuple USING gist (key);
+step flush: SELECT FROM pg_stat_force_next_flush();
+step disable_seq: SET enable_seqscan = false;
+step disable_bitmap: SET enable_bitmapscan = false;
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step delete: DELETE FROM kill_prior_tuple;
+step flush: SELECT FROM pg_stat_force_next_flush();
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_gist on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step drop_table: DROP TABLE IF EXISTS kill_prior_tuple;
+step drop_ext_btree_gist: DROP EXTENSION btree_gist;
+
+starting permutation: create_table fill_500 create_hash flush disable_seq disable_bitmap measure access flush result measure access flush result delete flush measure access flush result measure access flush result drop_table
+step create_table: CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null);
+step fill_500: INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 500) g(i);
+step create_hash: CREATE INDEX kill_prior_tuple_hash ON kill_prior_tuple USING hash (key);
+step flush: SELECT FROM pg_stat_force_next_flush();
+step disable_seq: SET enable_seqscan = false;
+step disable_bitmap: SET enable_bitmapscan = false;
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_hash on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_hash on kill_prior_tuple (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step delete: DELETE FROM kill_prior_tuple;
+step flush: SELECT FROM pg_stat_force_next_flush();
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_hash on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+-------------------------------------------------------------------------------------
+Index Scan using kill_prior_tuple_hash on kill_prior_tuple (actual rows=0.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(3 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 0
+(1 row)
+
+step drop_table: DROP TABLE IF EXISTS kill_prior_tuple;
+
+starting permutation: create_table fill_500 create_ext_btree_gin create_gin flush disable_seq delete flush measure access flush result measure access flush result drop_table drop_ext_btree_gin
+step create_table: CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null);
+step fill_500: INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 500) g(i);
+step create_ext_btree_gin: CREATE EXTENSION btree_gin;
+step create_gin: CREATE INDEX kill_prior_tuple_gin ON kill_prior_tuple USING gin (key);
+step flush: SELECT FROM pg_stat_force_next_flush();
+step disable_seq: SET enable_seqscan = false;
+step delete: DELETE FROM kill_prior_tuple;
+step flush: SELECT FROM pg_stat_force_next_flush();
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------
+Bitmap Heap Scan on kill_prior_tuple (actual rows=0.00 loops=1)
+ Recheck Cond: (key = 1)
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on kill_prior_tuple_gin (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(6 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step measure: UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple');
+step access: EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1;
+QUERY PLAN
+--------------------------------------------------------------------------
+Bitmap Heap Scan on kill_prior_tuple (actual rows=0.00 loops=1)
+ Recheck Cond: (key = 1)
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on kill_prior_tuple_gin (actual rows=1.00 loops=1)
+ Index Cond: (key = 1)
+ Index Searches: 1
+(6 rows)
+
+step flush: SELECT FROM pg_stat_force_next_flush();
+step result: SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple';
+new_heap_accesses
+-----------------
+ 1
+(1 row)
+
+step drop_table: DROP TABLE IF EXISTS kill_prior_tuple;
+step drop_ext_btree_gin: DROP EXTENSION btree_gin;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index e3c669a29c7..4411d3c86dd 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -16,6 +16,7 @@ test: ri-trigger
test: partial-index
test: two-ids
test: multiple-row-versions
+test: index-killtuples
test: index-only-scan
test: index-only-bitmapscan
test: predicate-lock-hot-tuple
diff --git a/src/test/isolation/specs/index-killtuples.spec b/src/test/isolation/specs/index-killtuples.spec
new file mode 100644
index 00000000000..77fe8c689a7
--- /dev/null
+++ b/src/test/isolation/specs/index-killtuples.spec
@@ -0,0 +1,127 @@
+# Basic testing of killtuples / kill_prior_tuples / all_dead testing
+# for various index AMs
+#
+# This tests just enough to ensure that the kill* routines are actually
+# executed and does something approximately reasonable. It's *not* sufficient
+# testing for adding killitems support to a new AM!
+#
+# This doesn't really need to be an isolation test, it could be written as a
+# regular regression test. However, writing it as an isolation test ends up a
+# *lot* less verbose.
+
+setup
+{
+ CREATE TABLE counter(heap_accesses int);
+ INSERT INTO counter(heap_accesses) VALUES (0);
+}
+
+teardown
+{
+ DROP TABLE counter;
+}
+
+session s1
+# to ensure GUCs are reset
+setup { RESET ALL; }
+
+step disable_seq { SET enable_seqscan = false; }
+
+step disable_bitmap { SET enable_bitmapscan = false; }
+
+# use a temporary table to make sure no other session can interfere with
+# visibility determinations
+step create_table { CREATE TEMPORARY TABLE kill_prior_tuple(key int not null, cat text not null); }
+
+step fill_10 { INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 10) g(i); }
+
+step fill_500 { INSERT INTO kill_prior_tuple(key, cat) SELECT g.i, 'a' FROM generate_series(1, 500) g(i); }
+
+# column-less select to make output easier to read
+step flush { SELECT FROM pg_stat_force_next_flush(); }
+
+step measure { UPDATE counter SET heap_accesses = (SELECT heap_blks_read + heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'kill_prior_tuple'); }
+
+step result { SELECT heap_blks_read + heap_blks_hit - counter.heap_accesses AS new_heap_accesses FROM counter, pg_statio_all_tables WHERE relname = 'kill_prior_tuple'; }
+
+step access { EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF) SELECT * FROM kill_prior_tuple WHERE key = 1; }
+
+step delete { DELETE FROM kill_prior_tuple; }
+
+step drop_table { DROP TABLE IF EXISTS kill_prior_tuple; }
+
+### steps for testing btree indexes ###
+step create_btree { CREATE INDEX kill_prior_tuple_btree ON kill_prior_tuple USING btree (key); }
+
+### steps for testing gist indexes ###
+# Creating the extensions takes time, so we don't want to do so when testing
+# other AMs
+step create_ext_btree_gist { CREATE EXTENSION btree_gist; }
+step drop_ext_btree_gist { DROP EXTENSION btree_gist; }
+step create_gist { CREATE INDEX kill_prior_tuple_gist ON kill_prior_tuple USING gist (key); }
+
+### steps for testing gin indexes ###
+# See create_ext_btree_gist
+step create_ext_btree_gin { CREATE EXTENSION btree_gin; }
+step drop_ext_btree_gin { DROP EXTENSION btree_gin; }
+step create_gin { CREATE INDEX kill_prior_tuple_gin ON kill_prior_tuple USING gin (key); }
+
+### steps for testing hash indexes ###
+step create_hash { CREATE INDEX kill_prior_tuple_hash ON kill_prior_tuple USING hash (key); }
+
+
+# test killtuples with btree index
+permutation
+ create_table fill_500 create_btree flush
+ disable_seq disable_bitmap
+ # show each access to non-deleted tuple increments heap_blks_*
+ measure access flush result
+ measure access flush result
+ delete flush
+ # first access after accessing deleted tuple still needs to access heap
+ measure access flush result
+ # but after kill_prior_tuple did its thing, we shouldn't access heap anymore
+ measure access flush result
+ drop_table
+
+# Same as first permutation, except testing gist
+permutation
+ create_table fill_500 create_ext_btree_gist create_gist flush
+ disable_seq disable_bitmap
+ measure access flush result
+ measure access flush result
+ delete flush
+ measure access flush result
+ measure access flush result
+ drop_table drop_ext_btree_gist
+
+# Test gist, but with fewer rows - shows that killitems doesn't work anymore!
+permutation
+ create_table fill_10 create_ext_btree_gist create_gist flush
+ disable_seq disable_bitmap
+ measure access flush result
+ measure access flush result
+ delete flush
+ measure access flush result
+ measure access flush result
+ drop_table drop_ext_btree_gist
+
+# Same as first permutation, except testing hash
+permutation
+ create_table fill_500 create_hash flush
+ disable_seq disable_bitmap
+ measure access flush result
+ measure access flush result
+ delete flush
+ measure access flush result
+ measure access flush result
+ drop_table
+
+# # Similar to first permutation, except that gin does not have killtuples support
+permutation
+ create_table fill_500 create_ext_btree_gin create_gin flush
+ disable_seq
+ delete flush
+ measure access flush result
+ # will still fetch from heap
+ measure access flush result
+ drop_table drop_ext_btree_gin