summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/heap/pruneheap.c5
-rw-r--r--src/backend/catalog/system_views.sql2
-rw-r--r--src/backend/executor/execIndexing.c4
-rw-r--r--src/backend/nodes/bitmapset.c9
-rw-r--r--src/backend/nodes/nodeFuncs.c4
-rw-r--r--src/backend/nodes/outfuncs.c13
-rw-r--r--src/backend/nodes/params.c6
-rw-r--r--src/backend/nodes/readfuncs.c7
-rw-r--r--src/backend/nodes/tidbitmap.c16
-rw-r--r--src/backend/replication/logical/slotsync.c93
-rw-r--r--src/backend/storage/lmgr/lwlock.c2
-rw-r--r--src/backend/utils/activity/pgstat.c56
-rw-r--r--src/backend/utils/activity/pgstat_replslot.c32
-rw-r--r--src/backend/utils/adt/formatting.c10
-rw-r--r--src/backend/utils/adt/pg_locale_libc.c3
-rw-r--r--src/backend/utils/adt/pgstatfuncs.c18
-rw-r--r--src/backend/utils/misc/gen_guc_tables.pl48
-rw-r--r--src/backend/utils/time/snapmgr.c1
18 files changed, 218 insertions, 111 deletions
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 0d6311088ea..ac958d86374 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -461,7 +461,7 @@ heap_page_will_freeze(Relation relation, Buffer buffer,
* 'new_relmin_mxid' arguments are required when freezing. When
* HEAP_PAGE_PRUNE_FREEZE option is passed, we also set presult->all_visible
* and presult->all_frozen after determining whether or not to
- * opporunistically freeze, to indicate if the VM bits can be set. They are
+ * opportunistically freeze, to indicate if the VM bits can be set. They are
* always set to false when the HEAP_PAGE_PRUNE_FREEZE option is not passed,
* because at the moment only callers that also freeze need that information.
*
@@ -504,6 +504,9 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
prstate.vistest = params->vistest;
prstate.mark_unused_now =
(params->options & HEAP_PAGE_PRUNE_MARK_UNUSED_NOW) != 0;
+
+ /* cutoffs must be provided if we will attempt freezing */
+ Assert(!(params->options & HEAP_PAGE_PRUNE_FREEZE) || params->cutoffs);
prstate.attempt_freeze = (params->options & HEAP_PAGE_PRUNE_FREEZE) != 0;
prstate.cutoffs = params->cutoffs;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 95ad29a64b9..6fffdb9398e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1076,6 +1076,8 @@ CREATE VIEW pg_stat_replication_slots AS
s.mem_exceeded_count,
s.total_txns,
s.total_bytes,
+ s.slotsync_skip_count,
+ s.slotsync_skip_at,
s.stats_reset
FROM pg_replication_slots as r,
LATERAL pg_stat_get_replication_slot(slot_name) as s
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index d1cbab58799..dd323c9b9fd 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -945,9 +945,7 @@ retry:
ExecDropSingleTupleTableSlot(existing_slot);
#ifdef USE_INJECTION_POINTS
- if (conflict)
- INJECTION_POINT("check-exclusion-or-unique-constraint-conflict", NULL);
- else
+ if (!conflict)
INJECTION_POINT("check-exclusion-or-unique-constraint-no-conflict", NULL);
#endif
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index b4ecf0b0390..7b1e9d94103 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -538,7 +538,6 @@ bms_is_member(int x, const Bitmapset *a)
int
bms_member_index(Bitmapset *a, int x)
{
- int i;
int bitnum;
int wordnum;
int result = 0;
@@ -554,7 +553,7 @@ bms_member_index(Bitmapset *a, int x)
bitnum = BITNUM(x);
/* count bits in preceding words */
- for (i = 0; i < wordnum; i++)
+ for (int i = 0; i < wordnum; i++)
{
bitmapword w = a->words[i];
@@ -1306,7 +1305,6 @@ int
bms_next_member(const Bitmapset *a, int prevbit)
{
int nwords;
- int wordnum;
bitmapword mask;
Assert(bms_is_valid_set(a));
@@ -1316,7 +1314,7 @@ bms_next_member(const Bitmapset *a, int prevbit)
nwords = a->nwords;
prevbit++;
mask = (~(bitmapword) 0) << BITNUM(prevbit);
- for (wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++)
+ for (int wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++)
{
bitmapword w = a->words[wordnum];
@@ -1366,7 +1364,6 @@ bms_next_member(const Bitmapset *a, int prevbit)
int
bms_prev_member(const Bitmapset *a, int prevbit)
{
- int wordnum;
int ushiftbits;
bitmapword mask;
@@ -1391,7 +1388,7 @@ bms_prev_member(const Bitmapset *a, int prevbit)
ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1);
mask = (~(bitmapword) 0) >> ushiftbits;
- for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
+ for (int wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
{
bitmapword w = a->words[wordnum];
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index ede838cd40c..d228318dc72 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -4826,9 +4826,7 @@ planstate_walk_members(PlanState **planstates, int nplans,
planstate_tree_walker_callback walker,
void *context)
{
- int j;
-
- for (j = 0; j < nplans; j++)
+ for (int j = 0; j < nplans; j++)
{
if (PSWALK(planstates[j]))
return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d9fe21a07e0..0abca9f803b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -346,8 +346,7 @@ outBitmapset(StringInfo str, const Bitmapset *bms)
void
outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
{
- Size length,
- i;
+ Size length;
char *s;
length = datumGetSize(value, typbyval, typlen);
@@ -356,7 +355,7 @@ outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
{
s = (char *) (&value);
appendStringInfo(str, "%u [ ", (unsigned int) length);
- for (i = 0; i < (Size) sizeof(Datum); i++)
+ for (Size i = 0; i < (Size) sizeof(Datum); i++)
appendStringInfo(str, "%d ", (int) (s[i]));
appendStringInfoChar(str, ']');
}
@@ -368,7 +367,7 @@ outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
else
{
appendStringInfo(str, "%u [ ", (unsigned int) length);
- for (i = 0; i < length; i++)
+ for (Size i = 0; i < length; i++)
appendStringInfo(str, "%d ", (int) (s[i]));
appendStringInfoChar(str, ']');
}
@@ -434,8 +433,6 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
static void
_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
{
- int i;
-
WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
WRITE_UINT_FIELD(con_relid);
@@ -450,10 +447,10 @@ _outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
WRITE_INT_FIELD(nmatched_ri);
/* for compactness, just print the number of matches per column: */
appendStringInfoString(str, " :eclass");
- for (i = 0; i < node->nkeys; i++)
+ for (int i = 0; i < node->nkeys; i++)
appendStringInfo(str, " %d", (node->eclass[i] != NULL));
appendStringInfoString(str, " :rinfos");
- for (i = 0; i < node->nkeys; i++)
+ for (int i = 0; i < node->nkeys; i++)
appendStringInfo(str, " %d", list_length(node->rinfos[i]));
}
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index ec5946c5777..aeb8ace2c54 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -166,13 +166,12 @@ paramlist_param_ref(ParseState *pstate, ParamRef *pref)
Size
EstimateParamListSpace(ParamListInfo paramLI)
{
- int i;
Size sz = sizeof(int);
if (paramLI == NULL || paramLI->numParams <= 0)
return sz;
- for (i = 0; i < paramLI->numParams; i++)
+ for (int i = 0; i < paramLI->numParams; i++)
{
ParamExternData *prm;
ParamExternData prmdata;
@@ -229,7 +228,6 @@ void
SerializeParamList(ParamListInfo paramLI, char **start_address)
{
int nparams;
- int i;
/* Write number of parameters. */
if (paramLI == NULL || paramLI->numParams <= 0)
@@ -240,7 +238,7 @@ SerializeParamList(ParamListInfo paramLI, char **start_address)
*start_address += sizeof(int);
/* Write each parameter in turn. */
- for (i = 0; i < nparams; i++)
+ for (int i = 0; i < nparams; i++)
{
ParamExternData *prm;
ParamExternData prmdata;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 2f933e95cb9..9a8ca27ec10 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -599,8 +599,7 @@ parseNodeString(void)
Datum
readDatum(bool typbyval)
{
- Size length,
- i;
+ Size length;
int tokenLength;
const char *token;
Datum res;
@@ -623,7 +622,7 @@ readDatum(bool typbyval)
elog(ERROR, "byval datum but length = %zu", length);
res = (Datum) 0;
s = (char *) (&res);
- for (i = 0; i < (Size) sizeof(Datum); i++)
+ for (Size i = 0; i < (Size) sizeof(Datum); i++)
{
token = pg_strtok(&tokenLength);
s[i] = (char) atoi(token);
@@ -634,7 +633,7 @@ readDatum(bool typbyval)
else
{
s = (char *) palloc(length);
- for (i = 0; i < length; i++)
+ for (Size i = 0; i < length; i++)
{
token = pg_strtok(&tokenLength);
s[i] = (char) atoi(token);
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 23d97b3a6c8..1f83d0d55f5 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -369,10 +369,9 @@ tbm_add_tuples(TIDBitmap *tbm, const ItemPointerData *tids, int ntids,
{
BlockNumber currblk = InvalidBlockNumber;
PagetableEntry *page = NULL; /* only valid when currblk is valid */
- int i;
Assert(tbm->iterating == TBM_NOT_ITERATING);
- for (i = 0; i < ntids; i++)
+ for (int i = 0; i < ntids; i++)
{
BlockNumber blk = ItemPointerGetBlockNumber(tids + i);
OffsetNumber off = ItemPointerGetOffsetNumber(tids + i);
@@ -471,12 +470,11 @@ static void
tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage)
{
PagetableEntry *apage;
- int wordnum;
if (bpage->ischunk)
{
/* Scan b's chunk, mark each indicated page lossy in a */
- for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
+ for (int wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
{
bitmapword w = bpage->words[wordnum];
@@ -511,7 +509,7 @@ tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage)
else
{
/* Both pages are exact, merge at the bit level */
- for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
+ for (int wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
apage->words[wordnum] |= bpage->words[wordnum];
apage->recheck |= bpage->recheck;
}
@@ -579,14 +577,13 @@ static bool
tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b)
{
const PagetableEntry *bpage;
- int wordnum;
if (apage->ischunk)
{
/* Scan each bit in chunk, try to clear */
bool candelete = true;
- for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
+ for (int wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++)
{
bitmapword w = apage->words[wordnum];
@@ -640,7 +637,7 @@ tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b)
{
/* Both pages are exact, merge at the bit level */
Assert(!bpage->ischunk);
- for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
+ for (int wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
{
apage->words[wordnum] &= bpage->words[wordnum];
if (apage->words[wordnum] != 0)
@@ -904,10 +901,9 @@ tbm_extract_page_tuple(TBMIterateResult *iteritem,
uint32 max_offsets)
{
PagetableEntry *page = iteritem->internal_page;
- int wordnum;
int ntuples = 0;
- for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
+ for (int wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
{
bitmapword w = page->words[wordnum];
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 8b4afd87dc9..1f4f06d467b 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -187,6 +187,9 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
TransactionIdPrecedes(remote_slot->catalog_xmin,
slot->data.catalog_xmin))
{
+ /* Update slot sync skip stats */
+ pgstat_report_replslotsync(slot);
+
/*
* This can happen in following situations:
*
@@ -277,6 +280,13 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
errdetail_internal("Remote slot has LSN %X/%08X but local slot has LSN %X/%08X.",
LSN_FORMAT_ARGS(remote_slot->confirmed_lsn),
LSN_FORMAT_ARGS(slot->data.confirmed_flush)));
+
+ /*
+ * If we can't reach a consistent snapshot, the slot won't be
+ * persisted. See update_and_persist_local_synced_slot().
+ */
+ if (found_consistent_snapshot && !(*found_consistent_snapshot))
+ pgstat_report_replslotsync(slot);
}
updated_xmin_or_lsn = true;
@@ -563,6 +573,7 @@ update_and_persist_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid)
bool found_consistent_snapshot = false;
bool remote_slot_precedes = false;
+ /* Slotsync skip stats are handled in function update_local_synced_slot() */
(void) update_local_synced_slot(remote_slot, remote_dbid,
&found_consistent_snapshot,
&remote_slot_precedes);
@@ -624,31 +635,9 @@ static bool
synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid)
{
ReplicationSlot *slot;
- XLogRecPtr latestFlushPtr;
+ XLogRecPtr latestFlushPtr = GetStandbyFlushRecPtr(NULL);
bool slot_updated = false;
- /*
- * Make sure that concerned WAL is received and flushed before syncing
- * slot to target lsn received from the primary server.
- */
- latestFlushPtr = GetStandbyFlushRecPtr(NULL);
- if (remote_slot->confirmed_lsn > latestFlushPtr)
- {
- /*
- * Can get here only if GUC 'synchronized_standby_slots' on the
- * primary server was not configured correctly.
- */
- ereport(AmLogicalSlotSyncWorkerProcess() ? LOG : ERROR,
- errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("skipping slot synchronization because the received slot sync"
- " LSN %X/%08X for slot \"%s\" is ahead of the standby position %X/%08X",
- LSN_FORMAT_ARGS(remote_slot->confirmed_lsn),
- remote_slot->name,
- LSN_FORMAT_ARGS(latestFlushPtr)));
-
- return false;
- }
-
/* Search for the named slot */
if ((slot = SearchNamedReplicationSlot(remote_slot->name, true)))
{
@@ -707,10 +696,40 @@ synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid)
/* Skip the sync of an invalidated slot */
if (slot->data.invalidated != RS_INVAL_NONE)
{
+ pgstat_report_replslotsync(slot);
+
ReplicationSlotRelease();
return slot_updated;
}
+ /*
+ * Make sure that concerned WAL is received and flushed before syncing
+ * slot to target lsn received from the primary server.
+ *
+ * Report statistics only after the slot has been acquired, ensuring
+ * it cannot be dropped during the reporting process.
+ */
+ if (remote_slot->confirmed_lsn > latestFlushPtr)
+ {
+ pgstat_report_replslotsync(slot);
+
+ /*
+ * Can get here only if GUC 'synchronized_standby_slots' on the
+ * primary server was not configured correctly.
+ */
+ ereport(AmLogicalSlotSyncWorkerProcess() ? LOG : ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("skipping slot synchronization because the received slot sync"
+ " LSN %X/%08X for slot \"%s\" is ahead of the standby position %X/%08X",
+ LSN_FORMAT_ARGS(remote_slot->confirmed_lsn),
+ remote_slot->name,
+ LSN_FORMAT_ARGS(latestFlushPtr)));
+
+ ReplicationSlotRelease();
+
+ return slot_updated;
+ }
+
/* Slot not ready yet, let's attempt to make it sync-ready now. */
if (slot->data.persistency == RS_TEMPORARY)
{
@@ -784,6 +803,34 @@ synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid)
ReplicationSlotsComputeRequiredXmin(true);
LWLockRelease(ProcArrayLock);
+ /*
+ * Make sure that concerned WAL is received and flushed before syncing
+ * slot to target lsn received from the primary server.
+ *
+ * Report statistics only after the slot has been acquired, ensuring
+ * it cannot be dropped during the reporting process.
+ */
+ if (remote_slot->confirmed_lsn > latestFlushPtr)
+ {
+ pgstat_report_replslotsync(slot);
+
+ /*
+ * Can get here only if GUC 'synchronized_standby_slots' on the
+ * primary server was not configured correctly.
+ */
+ ereport(AmLogicalSlotSyncWorkerProcess() ? LOG : ERROR,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("skipping slot synchronization because the received slot sync"
+ " LSN %X/%08X for slot \"%s\" is ahead of the standby position %X/%08X",
+ LSN_FORMAT_ARGS(remote_slot->confirmed_lsn),
+ remote_slot->name,
+ LSN_FORMAT_ARGS(latestFlushPtr)));
+
+ ReplicationSlotRelease();
+
+ return false;
+ }
+
update_and_persist_local_synced_slot(remote_slot, remote_dbid);
slot_updated = true;
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index b017880f5e4..255cfa8fa95 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -998,7 +998,7 @@ LWLockWakeup(LWLock *lock)
else
desired_state &= ~LW_FLAG_RELEASE_OK;
- if (proclist_is_empty(&wakeup))
+ if (proclist_is_empty(&lock->waiters))
desired_state &= ~LW_FLAG_HAS_WAITERS;
desired_state &= ~LW_FLAG_LOCKED; /* release lock */
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 7ef06150df7..8713c7a0483 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -1551,20 +1551,18 @@ pgstat_assert_is_up(void)
* ------------------------------------------------------------
*/
-/* helpers for pgstat_write_statsfile() */
-static void
-write_chunk(FILE *fpout, void *ptr, size_t len)
+/* helper for pgstat_write_statsfile() */
+void
+pgstat_write_chunk(FILE *fpout, void *ptr, size_t len)
{
int rc;
rc = fwrite(ptr, len, 1, fpout);
- /* we'll check for errors with ferror once at the end */
+ /* We check for errors with ferror() when done writing the stats. */
(void) rc;
}
-#define write_chunk_s(fpout, ptr) write_chunk(fpout, ptr, sizeof(*ptr))
-
/*
* This function is called in the last process that is accessing the shared
* stats so locking is not required.
@@ -1606,7 +1604,7 @@ pgstat_write_statsfile(void)
* Write the file header --- currently just a format ID.
*/
format_id = PGSTAT_FILE_FORMAT_ID;
- write_chunk_s(fpout, &format_id);
+ pgstat_write_chunk_s(fpout, &format_id);
/* Write various stats structs for fixed number of objects */
for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
@@ -1631,8 +1629,8 @@ pgstat_write_statsfile(void)
ptr = pgStatLocal.snapshot.custom_data[kind - PGSTAT_KIND_CUSTOM_MIN];
fputc(PGSTAT_FILE_ENTRY_FIXED, fpout);
- write_chunk_s(fpout, &kind);
- write_chunk(fpout, ptr, info->shared_data_len);
+ pgstat_write_chunk_s(fpout, &kind);
+ pgstat_write_chunk(fpout, ptr, info->shared_data_len);
}
/*
@@ -1686,7 +1684,7 @@ pgstat_write_statsfile(void)
{
/* normal stats entry, identified by PgStat_HashKey */
fputc(PGSTAT_FILE_ENTRY_HASH, fpout);
- write_chunk_s(fpout, &ps->key);
+ pgstat_write_chunk_s(fpout, &ps->key);
}
else
{
@@ -1696,21 +1694,21 @@ pgstat_write_statsfile(void)
kind_info->to_serialized_name(&ps->key, shstats, &name);
fputc(PGSTAT_FILE_ENTRY_NAME, fpout);
- write_chunk_s(fpout, &ps->key.kind);
- write_chunk_s(fpout, &name);
+ pgstat_write_chunk_s(fpout, &ps->key.kind);
+ pgstat_write_chunk_s(fpout, &name);
}
/* Write except the header part of the entry */
- write_chunk(fpout,
- pgstat_get_entry_data(ps->key.kind, shstats),
- pgstat_get_entry_len(ps->key.kind));
+ pgstat_write_chunk(fpout,
+ pgstat_get_entry_data(ps->key.kind, shstats),
+ pgstat_get_entry_len(ps->key.kind));
}
dshash_seq_term(&hstat);
/*
* No more output to be done. Close the temp file and replace the old
* pgstat.stat with it. The ferror() check replaces testing for error
- * after each individual fputc or fwrite (in write_chunk()) above.
+ * after each individual fputc or fwrite (in pgstat_write_chunk()) above.
*/
fputc(PGSTAT_FILE_ENTRY_END, fpout);
@@ -1738,15 +1736,13 @@ pgstat_write_statsfile(void)
}
}
-/* helpers for pgstat_read_statsfile() */
-static bool
-read_chunk(FILE *fpin, void *ptr, size_t len)
+/* helper for pgstat_read_statsfile() */
+bool
+pgstat_read_chunk(FILE *fpin, void *ptr, size_t len)
{
return fread(ptr, 1, len, fpin) == len;
}
-#define read_chunk_s(fpin, ptr) read_chunk(fpin, ptr, sizeof(*ptr))
-
/*
* Reads in existing statistics file into memory.
*
@@ -1790,7 +1786,7 @@ pgstat_read_statsfile(void)
/*
* Verify it's of the expected format.
*/
- if (!read_chunk_s(fpin, &format_id))
+ if (!pgstat_read_chunk_s(fpin, &format_id))
{
elog(WARNING, "could not read format ID");
goto error;
@@ -1820,7 +1816,7 @@ pgstat_read_statsfile(void)
char *ptr;
/* entry for fixed-numbered stats */
- if (!read_chunk_s(fpin, &kind))
+ if (!pgstat_read_chunk_s(fpin, &kind))
{
elog(WARNING, "could not read stats kind for entry of type %c", t);
goto error;
@@ -1860,7 +1856,7 @@ pgstat_read_statsfile(void)
info->shared_data_off;
}
- if (!read_chunk(fpin, ptr, info->shared_data_len))
+ if (!pgstat_read_chunk(fpin, ptr, info->shared_data_len))
{
elog(WARNING, "could not read data of stats kind %u for entry of type %c with size %u",
kind, t, info->shared_data_len);
@@ -1881,7 +1877,7 @@ pgstat_read_statsfile(void)
if (t == PGSTAT_FILE_ENTRY_HASH)
{
/* normal stats entry, identified by PgStat_HashKey */
- if (!read_chunk_s(fpin, &key))
+ if (!pgstat_read_chunk_s(fpin, &key))
{
elog(WARNING, "could not read key for entry of type %c", t);
goto error;
@@ -1910,12 +1906,12 @@ pgstat_read_statsfile(void)
PgStat_Kind kind;
NameData name;
- if (!read_chunk_s(fpin, &kind))
+ if (!pgstat_read_chunk_s(fpin, &kind))
{
elog(WARNING, "could not read stats kind for entry of type %c", t);
goto error;
}
- if (!read_chunk_s(fpin, &name))
+ if (!pgstat_read_chunk_s(fpin, &name))
{
elog(WARNING, "could not read name of stats kind %u for entry of type %c",
kind, t);
@@ -1990,9 +1986,9 @@ pgstat_read_statsfile(void)
key.objid, t);
}
- if (!read_chunk(fpin,
- pgstat_get_entry_data(key.kind, header),
- pgstat_get_entry_len(key.kind)))
+ if (!pgstat_read_chunk(fpin,
+ pgstat_get_entry_data(key.kind, header),
+ pgstat_get_entry_len(key.kind)))
{
elog(WARNING, "could not read data for entry %u/%u/%" PRIu64 " of type %c",
key.kind, key.dboid,
diff --git a/src/backend/utils/activity/pgstat_replslot.c b/src/backend/utils/activity/pgstat_replslot.c
index d210c261ac6..f93179146c2 100644
--- a/src/backend/utils/activity/pgstat_replslot.c
+++ b/src/backend/utils/activity/pgstat_replslot.c
@@ -103,6 +103,36 @@ pgstat_report_replslot(ReplicationSlot *slot, const PgStat_StatReplSlotEntry *re
}
/*
+ * Report replication slot sync skip statistics.
+ *
+ * Similar to pgstat_report_replslot(), we can rely on the stats for the
+ * slot to exist and to belong to this slot.
+ */
+void
+pgstat_report_replslotsync(ReplicationSlot *slot)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_ReplSlot *shstatent;
+ PgStat_StatReplSlotEntry *statent;
+
+ /* Slot sync stats are valid only for logical slots on standby. */
+ Assert(SlotIsLogical(slot));
+ Assert(RecoveryInProgress());
+
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
+ ReplicationSlotIndex(slot), false);
+ Assert(entry_ref != NULL);
+
+ shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
+ statent = &shstatent->stats;
+
+ statent->slotsync_skip_count += 1;
+ statent->slotsync_skip_at = GetCurrentTimestamp();
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
* Report replication slot creation.
*
* NB: This gets called with ReplicationSlotAllocationLock already held, be
@@ -133,7 +163,7 @@ pgstat_create_replslot(ReplicationSlot *slot)
* Report replication slot has been acquired.
*
* This guarantees that a stats entry exists during later
- * pgstat_report_replslot() calls.
+ * pgstat_report_replslot() or pgstat_report_replslotsync() calls.
*
* If we previously crashed, no stats data exists. But if we did not crash,
* the stats do belong to this slot:
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 5f7b3114da7..5bfeda2ffde 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1617,16 +1617,6 @@ str_numth(char *dest, const char *num, enum TH_Case type)
*****************************************************************************/
/*
- * If the system provides the needed functions for wide-character manipulation
- * (which are all standardized by C99), then we implement upper/lower/initcap
- * using wide-character functions, if necessary. Otherwise we use the
- * traditional <ctype.h> functions, which of course will not work as desired
- * in multibyte character sets. Note that in either case we are effectively
- * assuming that the database character encoding matches the encoding implied
- * by LC_CTYPE.
- */
-
-/*
* collation-aware, wide-character-aware lower function
*
* We pass the number of bytes so we can pass varlena and char*
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index 716f005066a..abf27283a33 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -434,9 +434,6 @@ strlower_libc_sb(char *dest, size_t destsize, const char *src, ssize_t srclen,
locale_t loc = locale->lt;
char *p;
- if (srclen + 1 > destsize)
- return srclen;
-
memcpy(dest, src, srclen);
dest[srclen] = '\0';
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 3d98d064a94..7e2ed69138a 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -2129,7 +2129,7 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
Datum
pg_stat_get_replication_slot(PG_FUNCTION_ARGS)
{
-#define PG_STAT_GET_REPLICATION_SLOT_COLS 11
+#define PG_STAT_GET_REPLICATION_SLOT_COLS 13
text *slotname_text = PG_GETARG_TEXT_P(0);
NameData slotname;
TupleDesc tupdesc;
@@ -2160,7 +2160,11 @@ pg_stat_get_replication_slot(PG_FUNCTION_ARGS)
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "total_bytes",
INT8OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 11, "stats_reset",
+ TupleDescInitEntry(tupdesc, (AttrNumber) 11, "slotsync_skip_count",
+ INT8OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 12, "slotsync_skip_at",
+ TIMESTAMPTZOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 13, "stats_reset",
TIMESTAMPTZOID, -1, 0);
BlessTupleDesc(tupdesc);
@@ -2186,11 +2190,17 @@ pg_stat_get_replication_slot(PG_FUNCTION_ARGS)
values[7] = Int64GetDatum(slotent->mem_exceeded_count);
values[8] = Int64GetDatum(slotent->total_txns);
values[9] = Int64GetDatum(slotent->total_bytes);
+ values[10] = Int64GetDatum(slotent->slotsync_skip_count);
+
+ if (slotent->slotsync_skip_at == 0)
+ nulls[11] = true;
+ else
+ values[11] = TimestampTzGetDatum(slotent->slotsync_skip_at);
if (slotent->stat_reset_timestamp == 0)
- nulls[10] = true;
+ nulls[12] = true;
else
- values[10] = TimestampTzGetDatum(slotent->stat_reset_timestamp);
+ values[12] = TimestampTzGetDatum(slotent->stat_reset_timestamp);
/* Returns the record as Datum */
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
diff --git a/src/backend/utils/misc/gen_guc_tables.pl b/src/backend/utils/misc/gen_guc_tables.pl
index 601c34ec30b..562819b7832 100644
--- a/src/backend/utils/misc/gen_guc_tables.pl
+++ b/src/backend/utils/misc/gen_guc_tables.pl
@@ -38,6 +38,52 @@ sub dquote
return q{"} . $s =~ s/"/\\"/gr . q{"};
}
+sub validate_guc_entry
+{
+ my ($entry) = @_;
+
+ my @required_common =
+ qw(name type context group short_desc variable boot_val);
+
+ my %required_by_type = (
+ int => [qw(min max)],
+ real => [qw(min max)],
+ enum => [qw(options)],
+ bool => [], # no extra required fields
+ string => [], # no extra required fields
+ );
+
+ for my $f (@required_common)
+ {
+ unless (defined $entry->{$f})
+ {
+ die sprintf(
+ qq{%s:%d: error: entry "%s" is missing required field "%s"\n},
+ $input_fname, $entry->{line_number},
+ $entry->{name} // '<unknown>', $f);
+ }
+ }
+
+ unless (exists $required_by_type{ $entry->{type} })
+ {
+ die sprintf(
+ qq{%s:%d: error: entry "%s" has unrecognized GUC type "%s"\n},
+ $input_fname, $entry->{line_number},
+ $entry->{name}, $entry->{type} // '<unknown>');
+ }
+
+ for my $f (@{ $required_by_type{ $entry->{type} } })
+ {
+ unless (defined $entry->{$f})
+ {
+ die sprintf(
+ qq{%s:%d: error: entry "%s" of type "%s" is missing required field "%s"\n},
+ $input_fname, $entry->{line_number}, $entry->{name},
+ $entry->{type}, $f);
+ }
+ }
+}
+
# Print GUC table.
sub print_table
{
@@ -50,6 +96,8 @@ sub print_table
foreach my $entry (@{$parse})
{
+ validate_guc_entry($entry);
+
if (defined($prev_name) && lc($prev_name) ge lc($entry->{name}))
{
die sprintf(
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 24f73a49d27..434abbf6b6f 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -459,6 +459,7 @@ InvalidateCatalogSnapshot(void)
pairingheap_remove(&RegisteredSnapshots, &CatalogSnapshot->ph_node);
CatalogSnapshot = NULL;
SnapshotResetXmin();
+ INJECTION_POINT("pre-invalidate-catalog-snapshot-end", NULL);
INJECTION_POINT("invalidate-catalog-snapshot-end", NULL);
}
}