diff options
Diffstat (limited to 'src')
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; |