summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/gin/gininsert.c22
-rw-r--r--src/backend/access/rmgrdesc/gindesc.c18
-rw-r--r--src/backend/catalog/system_views.sql12
-rw-r--r--src/backend/commands/sequence.c10
-rw-r--r--src/backend/utils/activity/pgstat.c1
-rw-r--r--src/backend/utils/activity/pgstat_relation.c6
-rw-r--r--src/backend/utils/adt/encode.c137
-rw-r--r--src/backend/utils/adt/numeric.c4
-rw-r--r--src/backend/utils/adt/pgstatfuncs.c3
-rw-r--r--src/include/access/ginxlog.h3
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.dat8
-rw-r--r--src/include/pgstat.h4
-rw-r--r--src/include/port/simd.h211
-rw-r--r--src/include/utils/pgstat_internal.h1
-rw-r--r--src/interfaces/libpq/fe-secure-gssapi.c27
-rw-r--r--src/test/regress/expected/rules.out36
-rw-r--r--src/test/regress/expected/sequence.out8
-rw-r--r--src/test/regress/expected/stats.out39
-rw-r--r--src/test/regress/expected/strings.out58
-rw-r--r--src/test/regress/sql/sequence.sql2
-rw-r--r--src/test/regress/sql/stats.sql16
-rw-r--r--src/test/regress/sql/strings.sql16
23 files changed, 569 insertions, 75 deletions
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index e9d4b27427e..1d3ab22556d 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -218,7 +218,8 @@ addItemPointersToLeafTuple(GinState *ginstate,
ItemPointerData *newItems,
*oldItems;
int oldNPosting,
- newNPosting;
+ newNPosting,
+ nwritten;
GinPostingList *compressedList;
Assert(!GinIsPostingTree(old));
@@ -235,18 +236,19 @@ addItemPointersToLeafTuple(GinState *ginstate,
/* Compress the posting list, and try to a build tuple with room for it */
res = NULL;
- compressedList = ginCompressPostingList(newItems, newNPosting, GinMaxItemSize,
- NULL);
- pfree(newItems);
- if (compressedList)
+ compressedList = ginCompressPostingList(newItems, newNPosting, GinMaxItemSize, &nwritten);
+ if (nwritten == newNPosting)
{
res = GinFormTuple(ginstate, attnum, key, category,
(char *) compressedList,
SizeOfGinPostingList(compressedList),
newNPosting,
false);
- pfree(compressedList);
}
+
+ pfree(newItems);
+ pfree(compressedList);
+
if (!res)
{
/* posting list would be too big, convert to posting tree */
@@ -293,17 +295,19 @@ buildFreshLeafTuple(GinState *ginstate,
{
IndexTuple res = NULL;
GinPostingList *compressedList;
+ int nwritten;
/* try to build a posting list tuple with all the items */
- compressedList = ginCompressPostingList(items, nitem, GinMaxItemSize, NULL);
- if (compressedList)
+ compressedList = ginCompressPostingList(items, nitem, GinMaxItemSize, &nwritten);
+ if (nwritten == nitem)
{
res = GinFormTuple(ginstate, attnum, key, category,
(char *) compressedList,
SizeOfGinPostingList(compressedList),
nitem, false);
- pfree(compressedList);
}
+ pfree(compressedList);
+
if (!res)
{
/* posting list would be too big, build posting tree */
diff --git a/src/backend/access/rmgrdesc/gindesc.c b/src/backend/access/rmgrdesc/gindesc.c
index 723ff9499cf..229675775ff 100644
--- a/src/backend/access/rmgrdesc/gindesc.c
+++ b/src/backend/access/rmgrdesc/gindesc.c
@@ -99,14 +99,7 @@ gin_desc(StringInfo buf, XLogReaderState *record)
appendStringInfo(buf, " children: %u/%u",
leftChildBlkno, rightChildBlkno);
}
- if (XLogRecHasBlockImage(record, 0))
- {
- if (XLogRecBlockImageApply(record, 0))
- appendStringInfoString(buf, " (full page image)");
- else
- appendStringInfoString(buf, " (full page image, for WAL verification)");
- }
- else
+ if (!XLogRecHasBlockImage(record, 0))
{
char *payload = XLogRecGetBlockData(record, 0, NULL);
@@ -144,14 +137,7 @@ gin_desc(StringInfo buf, XLogReaderState *record)
break;
case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
{
- if (XLogRecHasBlockImage(record, 0))
- {
- if (XLogRecBlockImageApply(record, 0))
- appendStringInfoString(buf, " (full page image)");
- else
- appendStringInfoString(buf, " (full page image, for WAL verification)");
- }
- else
+ if (!XLogRecHasBlockImage(record, 0))
{
ginxlogVacuumDataLeafPage *xlrec =
(ginxlogVacuumDataLeafPage *) XLogRecGetBlockData(record, 0, NULL);
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index c77fa0234bb..884b6a23817 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -716,7 +716,8 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
- pg_stat_get_total_autoanalyze_time(C.oid) AS total_autoanalyze_time
+ pg_stat_get_total_autoanalyze_time(C.oid) AS total_autoanalyze_time,
+ pg_stat_get_stat_reset_time(C.oid) AS stats_reset
FROM pg_class C LEFT JOIN
pg_index I ON C.oid = I.indrelid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -778,7 +779,8 @@ CREATE VIEW pg_statio_all_tables AS
pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
X.idx_blks_read AS tidx_blks_read,
- X.idx_blks_hit AS tidx_blks_hit
+ X.idx_blks_hit AS tidx_blks_hit,
+ pg_stat_get_stat_reset_time(C.oid) AS stats_reset
FROM pg_class C LEFT JOIN
pg_class T ON C.reltoastrelid = T.oid
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -818,7 +820,8 @@ CREATE VIEW pg_stat_all_indexes AS
pg_stat_get_numscans(I.oid) AS idx_scan,
pg_stat_get_lastscan(I.oid) AS last_idx_scan,
pg_stat_get_tuples_returned(I.oid) AS idx_tup_read,
- pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch
+ pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch,
+ pg_stat_get_stat_reset_time(I.oid) AS stats_reset
FROM pg_class C JOIN
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
@@ -844,7 +847,8 @@ CREATE VIEW pg_statio_all_indexes AS
I.relname AS indexrelname,
pg_stat_get_blocks_fetched(I.oid) -
pg_stat_get_blocks_hit(I.oid) AS idx_blks_read,
- pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit
+ pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit,
+ pg_stat_get_stat_reset_time(I.oid) AS stats_reset
FROM pg_class C JOIN
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 636d3c3ec73..cf46a543364 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -45,6 +45,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/pg_lsn.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
@@ -1795,7 +1796,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
/*
- * Return the sequence tuple.
+ * Return the sequence tuple along with its page LSN.
*
* This is primarily intended for use by pg_dump to gather sequence data
* without needing to individually query each sequence relation.
@@ -1803,7 +1804,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
Datum
pg_get_sequence_data(PG_FUNCTION_ARGS)
{
-#define PG_GET_SEQUENCE_DATA_COLS 2
+#define PG_GET_SEQUENCE_DATA_COLS 3
Oid relid = PG_GETARG_OID(0);
SeqTable elm;
Relation seqrel;
@@ -1818,6 +1819,8 @@ pg_get_sequence_data(PG_FUNCTION_ARGS)
INT8OID, -1, 0);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "is_called",
BOOLOID, -1, 0);
+ TupleDescInitEntry(resultTupleDesc, (AttrNumber) 3, "page_lsn",
+ LSNOID, -1, 0);
resultTupleDesc = BlessTupleDesc(resultTupleDesc);
init_sequence(relid, &elm, &seqrel);
@@ -1833,11 +1836,14 @@ pg_get_sequence_data(PG_FUNCTION_ARGS)
Buffer buf;
HeapTupleData seqtuple;
Form_pg_sequence_data seq;
+ Page page;
seq = read_seq_tuple(seqrel, &buf, &seqtuple);
+ page = BufferGetPage(buf);
values[0] = Int64GetDatum(seq->last_value);
values[1] = BoolGetDatum(seq->is_called);
+ values[2] = LSNGetDatum(PageGetLSN(page));
UnlockReleaseBuffer(buf);
}
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 44621653d8d..48f57e408e1 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -313,6 +313,7 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
.flush_pending_cb = pgstat_relation_flush_cb,
.delete_pending_cb = pgstat_relation_delete_pending_cb,
+ .reset_timestamp_cb = pgstat_relation_reset_timestamp_cb,
},
[PGSTAT_KIND_FUNCTION] = {
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index b5e3903a290..1de477cbeeb 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -910,6 +910,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
pgstat_unlink_relation(pending->relation);
}
+void
+pgstat_relation_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
+{
+ ((PgStatShared_Relation *) header)->stats.stat_reset_time = ts;
+}
+
/*
* Find or create a PgStat_TableStatus entry for rel. New entry is created and
* initialized if not exists.
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 9a9c7e8da99..aabe9913eee 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -16,6 +16,7 @@
#include <ctype.h>
#include "mb/pg_wchar.h"
+#include "port/simd.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "varatt.h"
@@ -177,8 +178,8 @@ static const int8 hexlookup[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
-uint64
-hex_encode(const char *src, size_t len, char *dst)
+static inline uint64
+hex_encode_scalar(const char *src, size_t len, char *dst)
{
const char *end = src + len;
@@ -193,6 +194,55 @@ hex_encode(const char *src, size_t len, char *dst)
return (uint64) len * 2;
}
+uint64
+hex_encode(const char *src, size_t len, char *dst)
+{
+#ifdef USE_NO_SIMD
+ return hex_encode_scalar(src, len, dst);
+#else
+ const uint64 tail_idx = len & ~(sizeof(Vector8) - 1);
+ uint64 i;
+
+ /*
+ * This splits the high and low nibbles of each byte into separate
+ * vectors, adds the vectors to a mask that converts the nibbles to their
+ * equivalent ASCII bytes, and interleaves those bytes back together to
+ * form the final hex-encoded string.
+ */
+ for (i = 0; i < tail_idx; i += sizeof(Vector8))
+ {
+ Vector8 srcv;
+ Vector8 lo;
+ Vector8 hi;
+ Vector8 mask;
+
+ vector8_load(&srcv, (const uint8 *) &src[i]);
+
+ lo = vector8_and(srcv, vector8_broadcast(0x0f));
+ mask = vector8_gt(lo, vector8_broadcast(0x9));
+ mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
+ mask = vector8_add(mask, vector8_broadcast('0'));
+ lo = vector8_add(lo, mask);
+
+ hi = vector8_and(srcv, vector8_broadcast(0xf0));
+ hi = vector8_shift_right(hi, 4);
+ mask = vector8_gt(hi, vector8_broadcast(0x9));
+ mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
+ mask = vector8_add(mask, vector8_broadcast('0'));
+ hi = vector8_add(hi, mask);
+
+ vector8_store((uint8 *) &dst[i * 2],
+ vector8_interleave_low(hi, lo));
+ vector8_store((uint8 *) &dst[i * 2 + sizeof(Vector8)],
+ vector8_interleave_high(hi, lo));
+ }
+
+ (void) hex_encode_scalar(src + i, len - i, dst + i * 2);
+
+ return (uint64) len * 2;
+#endif
+}
+
static inline bool
get_hex(const char *cp, char *out)
{
@@ -213,8 +263,8 @@ hex_decode(const char *src, size_t len, char *dst)
return hex_decode_safe(src, len, dst, NULL);
}
-uint64
-hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+static inline uint64
+hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
{
const char *s,
*srcend;
@@ -254,6 +304,85 @@ hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
return p - dst;
}
+/*
+ * This helper converts each byte to its binary-equivalent nibble by
+ * subtraction and combines them to form the return bytes (separated by zero
+ * bytes). Returns false if any input bytes are outside the expected ranges of
+ * ASCII values. Otherwise, returns true.
+ */
+#ifndef USE_NO_SIMD
+static inline bool
+hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
+{
+ Vector8 sub;
+ Vector8 mask_hi = vector8_interleave_low(vector8_broadcast(0), vector8_broadcast(0x0f));
+ Vector8 mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_broadcast(0));
+ Vector8 tmp;
+ bool ret;
+
+ tmp = vector8_gt(vector8_broadcast('9' + 1), src);
+ sub = vector8_and(tmp, vector8_broadcast('0'));
+
+ tmp = vector8_gt(src, vector8_broadcast('A' - 1));
+ tmp = vector8_and(tmp, vector8_broadcast('A' - 10));
+ sub = vector8_add(sub, tmp);
+
+ tmp = vector8_gt(src, vector8_broadcast('a' - 1));
+ tmp = vector8_and(tmp, vector8_broadcast('a' - 'A'));
+ sub = vector8_add(sub, tmp);
+
+ *dst = vector8_issub(src, sub);
+ ret = !vector8_has_ge(*dst, 0x10);
+
+ tmp = vector8_and(*dst, mask_hi);
+ tmp = vector8_shift_right(tmp, 8);
+ *dst = vector8_and(*dst, mask_lo);
+ *dst = vector8_shift_left(*dst, 4);
+ *dst = vector8_or(*dst, tmp);
+ return ret;
+}
+#endif /* ! USE_NO_SIMD */
+
+uint64
+hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+{
+#ifdef USE_NO_SIMD
+ return hex_decode_safe_scalar(src, len, dst, escontext);
+#else
+ const uint64 tail_idx = len & ~(sizeof(Vector8) * 2 - 1);
+ uint64 i;
+ bool success = true;
+
+ /*
+ * We must process 2 vectors at a time since the output will be half the
+ * length of the input.
+ */
+ for (i = 0; i < tail_idx; i += sizeof(Vector8) * 2)
+ {
+ Vector8 srcv;
+ Vector8 dstv1;
+ Vector8 dstv2;
+
+ vector8_load(&srcv, (const uint8 *) &src[i]);
+ success &= hex_decode_simd_helper(srcv, &dstv1);
+
+ vector8_load(&srcv, (const uint8 *) &src[i + sizeof(Vector8)]);
+ success &= hex_decode_simd_helper(srcv, &dstv2);
+
+ vector8_store((uint8 *) &dst[i / 2], vector8_pack_16(dstv1, dstv2));
+ }
+
+ /*
+ * If something didn't look right in the vector path, try again in the
+ * scalar path so that we can handle it correctly.
+ */
+ if (!success)
+ i = 0;
+
+ return i / 2 + hex_decode_safe_scalar(src + i, len - i, dst + i / 2, escontext);
+#endif
+}
+
static uint64
hex_enc_len(const char *src, size_t srclen)
{
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 76269918593..2501007d981 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4359,7 +4359,7 @@ int4_numeric(PG_FUNCTION_ARGS)
}
/*
- * Internal version of int4_numeric() with support for soft error reporting.
+ * Internal version of numeric_int4() with support for soft error reporting.
*/
int32
numeric_int4_safe(Numeric num, Node *escontext)
@@ -4429,7 +4429,7 @@ int8_numeric(PG_FUNCTION_ARGS)
}
/*
- * Internal version of int8_numeric() with support for soft error reporting.
+ * Internal version of numeric_int8() with support for soft error reporting.
*/
int64
numeric_int8_safe(Numeric num, Node *escontext)
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index c756c2bebaa..7e89a8048d5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -168,6 +168,9 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_stat_reset_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
+
Datum
pg_stat_get_function_calls(PG_FUNCTION_ARGS)
{
diff --git a/src/include/access/ginxlog.h b/src/include/access/ginxlog.h
index f8c671c2e0d..98760bf6ee4 100644
--- a/src/include/access/ginxlog.h
+++ b/src/include/access/ginxlog.h
@@ -179,6 +179,9 @@ typedef struct ginxlogUpdateMeta
#define XLOG_GIN_INSERT_LISTPAGE 0x70
+/*
+ * Backup Blk 0: list page with inserted tuples
+ */
typedef struct ginxlogInsertListPage
{
BlockNumber rightlink;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4e7d5ceb7b8..642abe5217e 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202509291
+#define CATALOG_VERSION_NO 202510062
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 01eba3b5a19..7c20180637f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3436,8 +3436,8 @@
{ oid => '6427', descr => 'return sequence tuple, for use by pg_dump',
proname => 'pg_get_sequence_data', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'regclass',
- proallargtypes => '{regclass,int8,bool}', proargmodes => '{i,o,o}',
- proargnames => '{sequence_oid,last_value,is_called}',
+ proallargtypes => '{regclass,int8,bool,pg_lsn}', proargmodes => '{i,o,o,o}',
+ proargnames => '{sequence_oid,last_value,is_called,page_lsn}',
prosrc => 'pg_get_sequence_data' },
{ oid => '275', descr => 'return the next oid for a system table',
@@ -5537,6 +5537,10 @@
proname => 'pg_stat_get_lastscan', provolatile => 's', proparallel => 'r',
prorettype => 'timestamptz', proargtypes => 'oid',
prosrc => 'pg_stat_get_lastscan' },
+{ oid => '9127', descr => 'statistics: last reset for a relation',
+ proname => 'pg_stat_get_stat_reset_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_stat_reset_time' },
{ oid => '1929', descr => 'statistics: number of tuples read by seqscan',
proname => 'pg_stat_get_tuples_returned', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e4a59a30b8c..8e8adb01176 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -212,7 +212,7 @@ typedef struct PgStat_TableXactStatus
* ------------------------------------------------------------
*/
-#define PGSTAT_FILE_FORMAT_ID 0x01A5BCB7
+#define PGSTAT_FILE_FORMAT_ID 0x01A5BCB8
typedef struct PgStat_ArchiverStats
{
@@ -454,6 +454,8 @@ typedef struct PgStat_StatTabEntry
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
PgStat_Counter total_autoanalyze_time;
+
+ TimestampTz stat_reset_time;
} PgStat_StatTabEntry;
/* ------
diff --git a/src/include/port/simd.h b/src/include/port/simd.h
index 5f5737707a8..b0165b45861 100644
--- a/src/include/port/simd.h
+++ b/src/include/port/simd.h
@@ -128,6 +128,21 @@ vector32_load(Vector32 *v, const uint32 *s)
#endif /* ! USE_NO_SIMD */
/*
+ * Store a vector into the given memory address.
+ */
+#ifndef USE_NO_SIMD
+static inline void
+vector8_store(uint8 *s, Vector8 v)
+{
+#ifdef USE_SSE2
+ _mm_storeu_si128((Vector8 *) s, v);
+#elif defined(USE_NEON)
+ vst1q_u8(s, v);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
* Create a vector with all elements set to the same value.
*/
static inline Vector8
@@ -266,6 +281,25 @@ vector8_has_le(const Vector8 v, const uint8 c)
}
/*
+ * Returns true if any elements in the vector are greater than or equal to the
+ * given scalar.
+ */
+#ifndef USE_NO_SIMD
+static inline bool
+vector8_has_ge(const Vector8 v, const uint8 c)
+{
+#ifdef USE_SSE2
+ Vector8 umax = _mm_max_epu8(v, vector8_broadcast(c));
+ Vector8 cmpe = vector8_eq(umax, v);
+
+ return vector8_is_highbit_set(cmpe);
+#elif defined(USE_NEON)
+ return vmaxvq_u8(v) >= c;
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
* Return true if the high bit of any element is set
*/
static inline bool
@@ -360,6 +394,55 @@ vector32_or(const Vector32 v1, const Vector32 v2)
#endif /* ! USE_NO_SIMD */
/*
+ * Return the bitwise AND of the inputs.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_and(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_and_si128(v1, v2);
+#elif defined(USE_NEON)
+ return vandq_u8(v1, v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Return the result of adding the respective elements of the input vectors.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_add(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_add_epi8(v1, v2);
+#elif defined(USE_NEON)
+ return vaddq_u8(v1, v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Return the result of subtracting the respective elements of the input
+ * vectors using signed saturation (i.e., if the operation would yield a value
+ * less than -128, -128 is returned instead). For more information on
+ * saturation arithmetic, see
+ * https://en.wikipedia.org/wiki/Saturation_arithmetic
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_issub(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_subs_epi8(v1, v2);
+#elif defined(USE_NEON)
+ return (Vector8) vqsubq_s8((int8x16_t) v1, (int8x16_t) v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
* Return a vector with all bits set in each lane where the corresponding
* lanes in the inputs are equal.
*/
@@ -388,6 +471,23 @@ vector32_eq(const Vector32 v1, const Vector32 v2)
#endif /* ! USE_NO_SIMD */
/*
+ * Return a vector with all bits set for each lane of v1 that is greater than
+ * the corresponding lane of v2. NB: The comparison treats the elements as
+ * signed.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_gt(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_cmpgt_epi8(v1, v2);
+#elif defined(USE_NEON)
+ return vcgtq_s8((int8x16_t) v1, (int8x16_t) v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
* Given two vectors, return a vector with the minimum element of each.
*/
#ifndef USE_NO_SIMD
@@ -402,4 +502,115 @@ vector8_min(const Vector8 v1, const Vector8 v2)
}
#endif /* ! USE_NO_SIMD */
+/*
+ * Interleave elements of low halves (e.g., for SSE2, bits 0-63) of given
+ * vectors. Bytes 0, 2, 4, etc. use v1, and bytes 1, 3, 5, etc. use v2.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_interleave_low(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_unpacklo_epi8(v1, v2);
+#elif defined(USE_NEON)
+ return vzip1q_u8(v1, v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Interleave elements of high halves (e.g., for SSE2, bits 64-127) of given
+ * vectors. Bytes 0, 2, 4, etc. use v1, and bytes 1, 3, 5, etc. use v2.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_interleave_high(const Vector8 v1, const Vector8 v2)
+{
+#ifdef USE_SSE2
+ return _mm_unpackhi_epi8(v1, v2);
+#elif defined(USE_NEON)
+ return vzip2q_u8(v1, v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Pack 16-bit elements in the given vectors into a single vector of 8-bit
+ * elements. The first half of the return vector (e.g., for SSE2, bits 0-63)
+ * uses v1, and the second half (e.g., for SSE2, bits 64-127) uses v2.
+ *
+ * NB: The upper 8-bits of each 16-bit element must be zeros, else this will
+ * produce different results on different architectures.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_pack_16(const Vector8 v1, const Vector8 v2)
+{
+ Vector8 mask PG_USED_FOR_ASSERTS_ONLY;
+
+ mask = vector8_interleave_low(vector8_broadcast(0), vector8_broadcast(0xff));
+ Assert(!vector8_has_ge(vector8_and(v1, mask), 1));
+ Assert(!vector8_has_ge(vector8_and(v2, mask), 1));
+#ifdef USE_SSE2
+ return _mm_packus_epi16(v1, v2);
+#elif defined(USE_NEON)
+ return vuzp1q_u8(v1, v2);
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Unsigned shift left of each 32-bit element in the vector by "i" bits.
+ *
+ * XXX AArch64 requires an integer literal, so we have to list all expected
+ * values of "i" from all callers in a switch statement. If you add a new
+ * caller, be sure your expected values of "i" are handled.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_shift_left(const Vector8 v1, int i)
+{
+#ifdef USE_SSE2
+ return _mm_slli_epi32(v1, i);
+#elif defined(USE_NEON)
+ switch (i)
+ {
+ case 4:
+ return (Vector8) vshlq_n_u32((Vector32) v1, 4);
+ default:
+ Assert(false);
+ return vector8_broadcast(0);
+ }
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
+/*
+ * Unsigned shift right of each 32-bit element in the vector by "i" bits.
+ *
+ * XXX AArch64 requires an integer literal, so we have to list all expected
+ * values of "i" from all callers in a switch statement. If you add a new
+ * caller, be sure your expected values of "i" are handled.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_shift_right(const Vector8 v1, int i)
+{
+#ifdef USE_SSE2
+ return _mm_srli_epi32(v1, i);
+#elif defined(USE_NEON)
+ switch (i)
+ {
+ case 4:
+ return (Vector8) vshrq_n_u32((Vector32) v1, 4);
+ case 8:
+ return (Vector8) vshrq_n_u32((Vector32) v1, 8);
+ default:
+ Assert(false);
+ return vector8_broadcast(0);
+ }
+#endif
+}
+#endif /* ! USE_NO_SIMD */
+
#endif /* SIMD_H */
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 88d09ea20ba..dc42d8043b5 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -716,6 +716,7 @@ extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref);
+extern void pgstat_relation_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);
/*
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index bc9e1ce06fa..843b31e175f 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -121,7 +121,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
{
appendPQExpBufferStr(&conn->errorMessage,
"GSSAPI caller failed to retransmit all data needing to be retried\n");
- errno = EINVAL;
+ SOCK_ERRNO_SET(EINVAL);
return -1;
}
@@ -199,14 +199,14 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
if (major != GSS_S_COMPLETE)
{
pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn, major, minor);
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
goto cleanup;
}
if (conf_state == 0)
{
libpq_append_conn_error(conn, "outgoing GSSAPI message would not use confidentiality");
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
goto cleanup;
}
@@ -215,7 +215,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
libpq_append_conn_error(conn, "client tried to send oversize GSSAPI packet (%zu > %zu)",
(size_t) output.length,
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
goto cleanup;
}
@@ -341,7 +341,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
/* If we still haven't got the length, return to the caller */
if (PqGSSRecvLength < sizeof(uint32))
{
- errno = EWOULDBLOCK;
+ SOCK_ERRNO_SET(EWOULDBLOCK);
return -1;
}
}
@@ -354,7 +354,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
libpq_append_conn_error(conn, "oversize GSSAPI packet sent by the server (%zu > %zu)",
(size_t) input.length,
PQ_GSS_MAX_PACKET_SIZE - sizeof(uint32));
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
return -1;
}
@@ -373,7 +373,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
/* If we don't yet have the whole packet, return to the caller */
if (PqGSSRecvLength - sizeof(uint32) < input.length)
{
- errno = EWOULDBLOCK;
+ SOCK_ERRNO_SET(EWOULDBLOCK);
return -1;
}
@@ -393,7 +393,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
major, minor);
ret = -1;
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
goto cleanup;
}
@@ -401,7 +401,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len)
{
libpq_append_conn_error(conn, "incoming GSSAPI message did not use confidentiality");
ret = -1;
- errno = EIO; /* for lack of a better idea */
+ SOCK_ERRNO_SET(EIO); /* for lack of a better idea */
goto cleanup;
}
@@ -437,7 +437,8 @@ gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
*ret = pqsecure_raw_read(conn, recv_buffer, length);
if (*ret < 0)
{
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
+ SOCK_ERRNO == EINTR)
return PGRES_POLLING_READING;
else
return PGRES_POLLING_FAILED;
@@ -457,7 +458,8 @@ gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
*ret = pqsecure_raw_read(conn, recv_buffer, length);
if (*ret < 0)
{
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
+ SOCK_ERRNO == EINTR)
return PGRES_POLLING_READING;
else
return PGRES_POLLING_FAILED;
@@ -520,7 +522,8 @@ pqsecure_open_gss(PGconn *conn)
ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
if (ret < 0)
{
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ if (SOCK_ERRNO == EAGAIN || SOCK_ERRNO == EWOULDBLOCK ||
+ SOCK_ERRNO == EINTR)
return PGRES_POLLING_WRITING;
else
return PGRES_POLLING_FAILED;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 35e8aad7701..7f1cb3bb4af 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1798,7 +1798,8 @@ pg_stat_all_indexes| SELECT c.oid AS relid,
pg_stat_get_numscans(i.oid) AS idx_scan,
pg_stat_get_lastscan(i.oid) AS last_idx_scan,
pg_stat_get_tuples_returned(i.oid) AS idx_tup_read,
- pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch
+ pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch,
+ pg_stat_get_stat_reset_time(i.oid) AS stats_reset
FROM (((pg_class c
JOIN pg_index x ON ((c.oid = x.indrelid)))
JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -1833,7 +1834,8 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
- pg_stat_get_total_autoanalyze_time(c.oid) AS total_autoanalyze_time
+ pg_stat_get_total_autoanalyze_time(c.oid) AS total_autoanalyze_time,
+ pg_stat_get_stat_reset_time(c.oid) AS stats_reset
FROM ((pg_class c
LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2200,7 +2202,8 @@ pg_stat_sys_indexes| SELECT relid,
idx_scan,
last_idx_scan,
idx_tup_read,
- idx_tup_fetch
+ idx_tup_fetch,
+ stats_reset
FROM pg_stat_all_indexes
WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
pg_stat_sys_tables| SELECT relid,
@@ -2232,7 +2235,8 @@ pg_stat_sys_tables| SELECT relid,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
- total_autoanalyze_time
+ total_autoanalyze_time,
+ stats_reset
FROM pg_stat_all_tables
WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
pg_stat_user_functions| SELECT p.oid AS funcid,
@@ -2252,7 +2256,8 @@ pg_stat_user_indexes| SELECT relid,
idx_scan,
last_idx_scan,
idx_tup_read,
- idx_tup_fetch
+ idx_tup_fetch,
+ stats_reset
FROM pg_stat_all_indexes
WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
pg_stat_user_tables| SELECT relid,
@@ -2284,7 +2289,8 @@ pg_stat_user_tables| SELECT relid,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
- total_autoanalyze_time
+ total_autoanalyze_time,
+ stats_reset
FROM pg_stat_all_tables
WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
pg_stat_wal| SELECT wal_records,
@@ -2370,7 +2376,8 @@ pg_statio_all_indexes| SELECT c.oid AS relid,
c.relname,
i.relname AS indexrelname,
(pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS idx_blks_read,
- pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit
+ pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit,
+ pg_stat_get_stat_reset_time(i.oid) AS stats_reset
FROM (((pg_class c
JOIN pg_index x ON ((c.oid = x.indrelid)))
JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -2394,7 +2401,8 @@ pg_statio_all_tables| SELECT c.oid AS relid,
(pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read,
pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit,
x.idx_blks_read AS tidx_blks_read,
- x.idx_blks_hit AS tidx_blks_hit
+ x.idx_blks_hit AS tidx_blks_hit,
+ pg_stat_get_stat_reset_time(c.oid) AS stats_reset
FROM ((((pg_class c
LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2413,7 +2421,8 @@ pg_statio_sys_indexes| SELECT relid,
relname,
indexrelname,
idx_blks_read,
- idx_blks_hit
+ idx_blks_hit,
+ stats_reset
FROM pg_statio_all_indexes
WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
pg_statio_sys_sequences| SELECT relid,
@@ -2433,7 +2442,8 @@ pg_statio_sys_tables| SELECT relid,
toast_blks_read,
toast_blks_hit,
tidx_blks_read,
- tidx_blks_hit
+ tidx_blks_hit,
+ stats_reset
FROM pg_statio_all_tables
WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
pg_statio_user_indexes| SELECT relid,
@@ -2442,7 +2452,8 @@ pg_statio_user_indexes| SELECT relid,
relname,
indexrelname,
idx_blks_read,
- idx_blks_hit
+ idx_blks_hit,
+ stats_reset
FROM pg_statio_all_indexes
WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
pg_statio_user_sequences| SELECT relid,
@@ -2462,7 +2473,8 @@ pg_statio_user_tables| SELECT relid,
toast_blks_read,
toast_blks_hit,
tidx_blks_read,
- tidx_blks_hit
+ tidx_blks_hit,
+ stats_reset
FROM pg_statio_all_tables
WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
pg_stats| SELECT n.nspname AS schemaname,
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 15925d99c8a..c4454e5b435 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -840,10 +840,10 @@ SELECT nextval('test_seq1');
(1 row)
-- pg_get_sequence_data
-SELECT * FROM pg_get_sequence_data('test_seq1');
- last_value | is_called
-------------+-----------
- 10 | t
+SELECT last_value, is_called, page_lsn <= pg_current_wal_lsn() as lsn FROM pg_get_sequence_data('test_seq1');
+ last_value | is_called | lsn
+------------+-----------+-----
+ 10 | t | t
(1 row)
DROP SEQUENCE test_seq1;
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 605f5070376..67e1860e984 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -666,16 +666,24 @@ SELECT last_seq_scan, last_idx_scan FROM pg_stat_all_tables WHERE relid = 'test_
(1 row)
COMMIT;
+SELECT stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
+ has_stats_reset
+-----------------
+ f
+(1 row)
+
SELECT pg_stat_reset_single_table_counters('test_last_scan'::regclass);
pg_stat_reset_single_table_counters
-------------------------------------
(1 row)
-SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
- seq_scan | idx_scan
-----------+----------
- 0 | 0
+SELECT seq_scan, idx_scan, stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
+ seq_scan | idx_scan | has_stats_reset
+----------+----------+-----------------
+ 0 | 0 | t
(1 row)
-- ensure we start out with exactly one index and sequential scan
@@ -850,6 +858,29 @@ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
2 | t | 3 | t
(1 row)
+-- check the stats in pg_stat_all_indexes
+SELECT idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok,
+ stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass;
+ idx_scan | idx_ok | has_stats_reset
+----------+--------+-----------------
+ 3 | t | f
+(1 row)
+
+-- check that the stats in pg_stat_all_indexes are reset
+SELECT pg_stat_reset_single_table_counters('test_last_scan_pkey'::regclass);
+ pg_stat_reset_single_table_counters
+-------------------------------------
+
+(1 row)
+
+SELECT idx_scan, stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass;
+ idx_scan | has_stats_reset
+----------+-----------------
+ 0 | t
+(1 row)
+
-----
-- Test reset of some stats for shared table
-----
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 691e475bce3..b9dc08d5f61 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -260,6 +260,64 @@ SELECT reverse('\xabcd'::bytea);
\xcdab
(1 row)
+SELECT ('\x' || repeat(' ', 32))::bytea;
+ bytea
+-------
+ \x
+(1 row)
+
+SELECT ('\x' || repeat('!', 32))::bytea;
+ERROR: invalid hexadecimal digit: "!"
+SELECT ('\x' || repeat('/', 34))::bytea;
+ERROR: invalid hexadecimal digit: "/"
+SELECT ('\x' || repeat('0', 34))::bytea;
+ bytea
+--------------------------------------
+ \x0000000000000000000000000000000000
+(1 row)
+
+SELECT ('\x' || repeat('9', 32))::bytea;
+ bytea
+------------------------------------
+ \x99999999999999999999999999999999
+(1 row)
+
+SELECT ('\x' || repeat(':', 32))::bytea;
+ERROR: invalid hexadecimal digit: ":"
+SELECT ('\x' || repeat('@', 34))::bytea;
+ERROR: invalid hexadecimal digit: "@"
+SELECT ('\x' || repeat('A', 34))::bytea;
+ bytea
+--------------------------------------
+ \xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+(1 row)
+
+SELECT ('\x' || repeat('F', 32))::bytea;
+ bytea
+------------------------------------
+ \xffffffffffffffffffffffffffffffff
+(1 row)
+
+SELECT ('\x' || repeat('G', 32))::bytea;
+ERROR: invalid hexadecimal digit: "G"
+SELECT ('\x' || repeat('`', 34))::bytea;
+ERROR: invalid hexadecimal digit: "`"
+SELECT ('\x' || repeat('a', 34))::bytea;
+ bytea
+--------------------------------------
+ \xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+(1 row)
+
+SELECT ('\x' || repeat('f', 32))::bytea;
+ bytea
+------------------------------------
+ \xffffffffffffffffffffffffffffffff
+(1 row)
+
+SELECT ('\x' || repeat('g', 32))::bytea;
+ERROR: invalid hexadecimal digit: "g"
+SELECT ('\x' || repeat('~', 34))::bytea;
+ERROR: invalid hexadecimal digit: "~"
SET bytea_output TO escape;
SELECT E'\\xDeAdBeEf'::bytea;
bytea
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 2c220b60749..b3344537929 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -414,6 +414,6 @@ SELECT nextval('test_seq1');
SELECT nextval('test_seq1');
-- pg_get_sequence_data
-SELECT * FROM pg_get_sequence_data('test_seq1');
+SELECT last_value, is_called, page_lsn <= pg_current_wal_lsn() as lsn FROM pg_get_sequence_data('test_seq1');
DROP SEQUENCE test_seq1;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 54e72866344..8768e0f27fd 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -312,8 +312,11 @@ SELECT pg_stat_force_next_flush();
SELECT last_seq_scan, last_idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
COMMIT;
+SELECT stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
SELECT pg_stat_reset_single_table_counters('test_last_scan'::regclass);
-SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
+SELECT seq_scan, idx_scan, stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
-- ensure we start out with exactly one index and sequential scan
BEGIN;
@@ -382,6 +385,17 @@ COMMIT;
SELECT seq_scan, :'test_last_seq' = last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
+-- check the stats in pg_stat_all_indexes
+SELECT idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok,
+ stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass;
+
+-- check that the stats in pg_stat_all_indexes are reset
+SELECT pg_stat_reset_single_table_counters('test_last_scan_pkey'::regclass);
+
+SELECT idx_scan, stats_reset IS NOT NULL AS has_stats_reset
+ FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass;
+
-----
-- Test reset of some stats for shared table
-----
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index c05f3413699..a2a91523404 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -82,6 +82,22 @@ SELECT reverse(''::bytea);
SELECT reverse('\xaa'::bytea);
SELECT reverse('\xabcd'::bytea);
+SELECT ('\x' || repeat(' ', 32))::bytea;
+SELECT ('\x' || repeat('!', 32))::bytea;
+SELECT ('\x' || repeat('/', 34))::bytea;
+SELECT ('\x' || repeat('0', 34))::bytea;
+SELECT ('\x' || repeat('9', 32))::bytea;
+SELECT ('\x' || repeat(':', 32))::bytea;
+SELECT ('\x' || repeat('@', 34))::bytea;
+SELECT ('\x' || repeat('A', 34))::bytea;
+SELECT ('\x' || repeat('F', 32))::bytea;
+SELECT ('\x' || repeat('G', 32))::bytea;
+SELECT ('\x' || repeat('`', 34))::bytea;
+SELECT ('\x' || repeat('a', 34))::bytea;
+SELECT ('\x' || repeat('f', 32))::bytea;
+SELECT ('\x' || repeat('g', 32))::bytea;
+SELECT ('\x' || repeat('~', 34))::bytea;
+
SET bytea_output TO escape;
SELECT E'\\xDeAdBeEf'::bytea;
SELECT E'\\x De Ad Be Ef '::bytea;