diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/access/heap/pruneheap.c | 5 | ||||
| -rw-r--r-- | src/backend/catalog/system_views.sql | 2 | ||||
| -rw-r--r-- | src/backend/executor/execIndexing.c | 4 | ||||
| -rw-r--r-- | src/backend/nodes/bitmapset.c | 9 | ||||
| -rw-r--r-- | src/backend/nodes/nodeFuncs.c | 4 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 13 | ||||
| -rw-r--r-- | src/backend/nodes/params.c | 6 | ||||
| -rw-r--r-- | src/backend/nodes/readfuncs.c | 7 | ||||
| -rw-r--r-- | src/backend/nodes/tidbitmap.c | 16 | ||||
| -rw-r--r-- | src/backend/replication/logical/slotsync.c | 93 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/lwlock.c | 2 | ||||
| -rw-r--r-- | src/backend/utils/activity/pgstat.c | 56 | ||||
| -rw-r--r-- | src/backend/utils/activity/pgstat_replslot.c | 32 | ||||
| -rw-r--r-- | src/backend/utils/adt/formatting.c | 10 | ||||
| -rw-r--r-- | src/backend/utils/adt/pg_locale_libc.c | 3 | ||||
| -rw-r--r-- | src/backend/utils/adt/pgstatfuncs.c | 18 | ||||
| -rw-r--r-- | src/backend/utils/misc/gen_guc_tables.pl | 48 | ||||
| -rw-r--r-- | src/backend/utils/time/snapmgr.c | 1 |
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); } } |
