Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vm7Tg-007iiS-0L for pgsql-hackers@arkaria.postgresql.org; Sat, 31 Jan 2026 09:41:16 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vm7Td-007l6X-0H for pgsql-hackers@arkaria.postgresql.org; Sat, 31 Jan 2026 09:41:13 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vm7Tc-007l6P-0V for pgsql-hackers@lists.postgresql.org; Sat, 31 Jan 2026 09:41:13 +0000 Received: from fout-a7-smtp.messagingengine.com ([103.168.172.150]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vm7TZ-000Iw8-2Y for pgsql-hackers@lists.postgresql.org; Sat, 31 Jan 2026 09:41:11 +0000 Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailfout.phl.internal (Postfix) with ESMTP id E2777EC008A for ; Sat, 31 Jan 2026 04:41:08 -0500 (EST) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-01.internal (MEProxy); Sat, 31 Jan 2026 04:41:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kurilemu.de; h= cc:content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:message-id:mime-version:reply-to:subject :subject:to:to; s=fm1; t=1769852468; x=1769938868; bh=F2UVPrCJMe 7/0zhcDfrPnS7sbFrc+XiFb+O5jo6tAXc=; b=RPdXTAmGJUjc98fxTR5Ta3IOTW 1Z8ubv1CqvenEdHeTdycG8I6gIifYuCIRlWB0WVjL/Br7n55iAMMoDgGfoNN+p56 bZ+Lazn3fG2lHm85T/eHgNPU57lGHHxRYVNC/TlSaBtezPMLsiZpZ8k5RnJlMlM2 Ixj/frhpnUj89p78xlENDf3AB4ygV31eXWEx88TvjyyDeSENZ69bclkzro6RRHFD aBEsXUnKFnpq6npsrZjpVnWCGOCsMaDxAgDZ6t+L6kIptOhnfT2qaoMyRc9caoc/ +VSvBJoEgOiswi+ofmlL9jT9gGEK+04I+a6mAQ1jD6lr7aHz+fZR6i1KtCRw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:message-id:mime-version:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1769852468; x=1769938868; bh=F2UVPrCJMe7/0zhcDfrPnS7sbFrc+XiFb+O 5jo6tAXc=; b=lim/vlo8ZHSR22VaLjIW2L34qEqM+Ve79EX4xG+pjQz+e6aocwA tP0CYESsMsykc5FAcYyvErBB4eS0M9nT6sQIeWz4iDAMiIA31LgprWDpcH7/6NO+ xKmbvqiDEUsx5UxWpaLLBqxVEgB8Wpnqv29zlxu7iHHH78e1rlMJSCZZ9y16YKKz OCCXlhle25+KcGHUW2YA2KTGlP7z9TQ4LNyGCG603Zeo/FdPr87gTQqtKEQ/KZZ/ CJcJylg+6wMnAqCohcBpWqerkncvgUvkL78fslxeHQt4TwJTFzOAACum+EhabRdb vjZykP970kHXMj3FmR7zdyU3RH32gPTAPxg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgddujeduheekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepfffhvffukfggtggugfesmhekreertddtjeenucfhrhhomheplmhlvhgrrhhoucfj vghrrhgvrhgruceorghlvhhhvghrrhgvsehkuhhrihhlvghmuhdruggvqeenucggtffrrg htthgvrhhnpeefveegkedttdeiueegtefgkedvgeevudegveeukeejtdejveejtefhfeeu jeeggeenucffohhmrghinhepvghnthgvrhhprhhishgvuggsrdgtohhmnecuvehluhhsth gvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprghlvhhhvghrrhgvsehk uhhrihhlvghmuhdruggvpdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuth dprhgtphhtthhopehpghhsqhhlqdhhrggtkhgvrhhssehlihhsthhsrdhpohhsthhgrhgv shhqlhdrohhrgh X-ME-Proxy: Feedback-ID: ie3de48e3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA for ; Sat, 31 Jan 2026 04:41:08 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kurilemu.de; s=schmee; t=1769852466; bh=j3QOFNU/TDCjtqvd1vHgj65eW6+E+P9FoGy35nSqjY0=; h=Date:From:To:Subject:From; b=2iNkVsxAYT+YC3DfE6v439jsmAzuCfLi7EJItTW2fhxSso0srCzCIfu+yoVOog1/F 7N58+OAGwk9rzz5wHcziIPnSzheOMSXdU3GZkTrlzrqJmyRG3Mt3gDCbQYGVZPRv7Z xMPMfO2U7jXwqxaMp4WHWTc3SdXHUY8aFQxx/o7aIph1p2+EHoMGJKlA+WLFqXzvxq AfzmfB09L+0wt1Y6NWuq6VjeTTn530Rat2KP5OfIzaPEPgjZeF4F+/G5olHTfwmTwp OpVBDJXhNhizk5JCdT/EiN69uuHq6RSuwVqVdQwamjVxHF4CpxPFGoczsjuqddv6LB 1UI+cV6LwlZvw== Received: by schmee.kurilemu.internal (Postfix, from userid 1000) id 74BFD74; Sat, 31 Jan 2026 10:41:06 +0100 (CET) Date: Sat, 31 Jan 2026 10:41:06 +0100 From: =?utf-8?Q?=C3=81lvaro?= Herrera To: Pg Hackers Subject: splitting pg_resetwal output strings Message-ID: <202601310924.yoik5n3blgt4@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="6po7oomtfoqj5cjo" Content-Disposition: inline Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --6po7oomtfoqj5cjo Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit Hello, For a long time I've been annoyed that certain tools such as (but not only) pg_resetwal have translatable strings full of whitespace that translators have to be careful about so that things look nice. For Spanish I have made the lines a bit wider so that everything fits, which means that every time someone adds a new line that's not yet translated, the output looks bad; and if I want to add one more space to align a new longer string, then I have to edit every single one of them. This is horrible. Here's a proposal. The idea is to have a separate file (entries.h right now but proposal for better names are welcome) which lists those strings, together with the printf specifiers needed to actually print them. This way, we can measure the length of each exactly as they translate before printing anything, and then line up everything to the same output length. One curious thing of note is that I had to add an internal_wcswidth() function, to avoid having to link libpq just to be able to do pg_wcswidth(). This is not complete. It modifies PrintControlValues(), which is easy because I just print each item in the entries.h file in the same order they appear there. But for PrintNewControlValues() I'll need to add a symbolic identifier to each string, that the code can use to scan the array and print just those elements. I'll do that if there are no objections to this idea here. Also, pg_controldata could probably something very similar or maybe the same entries.h file. -- Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/ "If you want to have good ideas, you must have many ideas. Most of them will be wrong, and what you have to learn is which ones to throw away." (Linus Pauling) --6po7oomtfoqj5cjo Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0001-Split-out-entry-names-in-pg_resetwal.patch" From 59403690ca6253835ce7577f3eee7d04893b6950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= Date: Fri, 14 Nov 2025 16:25:46 +0100 Subject: [PATCH] Split out entry names in pg_resetwal --- src/bin/pg_resetwal/entries.h | 62 ++++++++++++++ src/bin/pg_resetwal/nls.mk | 4 +- src/bin/pg_resetwal/pg_resetwal.c | 132 ++++++++++++++++-------------- 3 files changed, 134 insertions(+), 64 deletions(-) create mode 100644 src/bin/pg_resetwal/entries.h diff --git a/src/bin/pg_resetwal/entries.h b/src/bin/pg_resetwal/entries.h new file mode 100644 index 00000000000..6b7686da83a --- /dev/null +++ b/src/bin/pg_resetwal/entries.h @@ -0,0 +1,62 @@ +CONTROLDATA_LINE("pg_control version number", + "%u", ControlFile.pg_control_version) +CONTROLDATA_LINE("Catalog version number", + "%u", ControlFile.catalog_version_no) +CONTROLDATA_LINE("Database system identifier", + "%" PRIu64, ControlFile.system_identifier) +CONTROLDATA_LINE("Latest checkpoint's TimeLineID", + "%u", ControlFile.checkPointCopy.ThisTimeLineID) +CONTROLDATA_LINE("Latest checkpoint's full_page_writes", + "%s", (ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"))) +CONTROLDATA_LINE("Latest checkpoint's NextXID", + "%u:%u", EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid), XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid)) +CONTROLDATA_LINE("Latest checkpoint's NextOID", + "%u", ControlFile.checkPointCopy.nextOid) +CONTROLDATA_LINE("Latest checkpoint's NextMultiXactId", + "%u", ControlFile.checkPointCopy.nextMulti) +CONTROLDATA_LINE("Latest checkpoint's NextMultiOffset", + "%" PRIu64, ControlFile.checkPointCopy.nextMultiOffset) +CONTROLDATA_LINE("Latest checkpoint's oldestXID", + "%u", ControlFile.checkPointCopy.oldestXid) +CONTROLDATA_LINE("Latest checkpoint's oldestXID's DB", + "%u", ControlFile.checkPointCopy.oldestXidDB) +CONTROLDATA_LINE("Latest checkpoint's oldestActiveXID", + "%u", ControlFile.checkPointCopy.oldestActiveXid) +CONTROLDATA_LINE("Latest checkpoint's oldestMultiXid", + "%u", ControlFile.checkPointCopy.oldestMulti) +CONTROLDATA_LINE("Latest checkpoint's oldestMulti's DB", + "%u", ControlFile.checkPointCopy.oldestMultiDB) +CONTROLDATA_LINE("Latest checkpoint's oldestCommitTsXid", + "%u", ControlFile.checkPointCopy.oldestCommitTsXid) +CONTROLDATA_LINE("Latest checkpoint's newestCommitTsXid", + "%u", ControlFile.checkPointCopy.newestCommitTsXid) +CONTROLDATA_LINE("Maximum data alignment", + "%u", ControlFile.maxAlign) +CONTROLDATA_LINE("Database block size", + "%u", ControlFile.blcksz) +CONTROLDATA_LINE("Blocks per segment of large relation", + "%u", ControlFile.relseg_size) +CONTROLDATA_LINE("Pages per SLRU segment", + "%u", ControlFile.slru_pages_per_segment) +CONTROLDATA_LINE("WAL block size", + "%u", ControlFile.xlog_blcksz) +CONTROLDATA_LINE("Bytes per WAL segment", + "%u", ControlFile.xlog_seg_size) +CONTROLDATA_LINE("Maximum length of identifiers", + "%u", ControlFile.nameDataLen) +CONTROLDATA_LINE("Maximum columns in an index", + "%u", ControlFile.indexMaxKeys) +CONTROLDATA_LINE("Maximum size of a TOAST chunk", + "%u", ControlFile.toast_max_chunk_size) +CONTROLDATA_LINE("Size of a large-object chunk", + "%u", ControlFile.loblksize) + +/* This is no longer configurable, but users may still expect to see it: */ +CONTROLDATA_LINE("Date/time type storage", + "%s", _("64-bit integers")) +CONTROLDATA_LINE("Float8 argument passing", + "%s", ControlFile.float8ByVal ? _("by value") : _("by reference")) +CONTROLDATA_LINE("Data page checksum version", + "%u", ControlFile.data_checksum_version) +CONTROLDATA_LINE("Default char data signedness", + "%s", ControlFile.default_char_signedness ? _("signed") : _("unsigned")) diff --git a/src/bin/pg_resetwal/nls.mk b/src/bin/pg_resetwal/nls.mk index 694d5420a29..8dabee72bef 100644 --- a/src/bin/pg_resetwal/nls.mk +++ b/src/bin/pg_resetwal/nls.mk @@ -2,10 +2,12 @@ CATALOG_NAME = pg_resetwal GETTEXT_FILES = $(FRONTEND_COMMON_GETTEXT_FILES) \ pg_resetwal.c \ + entries.h \ ../../common/controldata_utils.c \ ../../common/fe_memutils.c \ ../../common/file_utils.c \ ../../common/restricted_token.c \ ../../fe_utils/option_utils.c -GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) +GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) \ + CONTROLDATA_LINE:1 GETTEXT_FLAGS = $(FRONTEND_COMMON_GETTEXT_FLAGS) diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 431b83a67da..fb17b6f80e0 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -57,6 +57,7 @@ #include "fe_utils/option_utils.h" #include "fe_utils/version.h" #include "getopt_long.h" +#include "mb/pg_wchar.h" #include "pg_getopt.h" #include "storage/large_object.h" @@ -114,6 +115,7 @@ static void KillExistingArchiveStatus(void); static void KillExistingWALSummaries(void); static void WriteEmptyXLOG(void); static void usage(void); +static int internal_wcswidth(const char *pwcs, size_t len, int encoding); static uint32 strtouint32_strict(const char *restrict s, char **restrict endptr, int base); static uint64 strtouint64_strict(const char *restrict s, char **restrict endptr, int base); @@ -753,74 +755,48 @@ GuessControlValues(void) static void PrintControlValues(bool guessed) { + int encoding = pg_get_encoding_from_locale(NULL, true); + int maxlen = 0; + int thislen; + if (guessed) printf(_("Guessed pg_control values:\n\n")); else printf(_("Current pg_control values:\n\n")); - printf(_("pg_control version number: %u\n"), - ControlFile.pg_control_version); - printf(_("Catalog version number: %u\n"), - ControlFile.catalog_version_no); - printf(_("Database system identifier: %" PRIu64 "\n"), - ControlFile.system_identifier); - printf(_("Latest checkpoint's TimeLineID: %u\n"), - ControlFile.checkPointCopy.ThisTimeLineID); - printf(_("Latest checkpoint's full_page_writes: %s\n"), - ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off")); - printf(_("Latest checkpoint's NextXID: %u:%u\n"), - EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid), - XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid)); - printf(_("Latest checkpoint's NextOID: %u\n"), - ControlFile.checkPointCopy.nextOid); - printf(_("Latest checkpoint's NextMultiXactId: %u\n"), - ControlFile.checkPointCopy.nextMulti); - printf(_("Latest checkpoint's NextMultiOffset: %" PRIu64 "\n"), - ControlFile.checkPointCopy.nextMultiOffset); - printf(_("Latest checkpoint's oldestXID: %u\n"), - ControlFile.checkPointCopy.oldestXid); - printf(_("Latest checkpoint's oldestXID's DB: %u\n"), - ControlFile.checkPointCopy.oldestXidDB); - printf(_("Latest checkpoint's oldestActiveXID: %u\n"), - ControlFile.checkPointCopy.oldestActiveXid); - printf(_("Latest checkpoint's oldestMultiXid: %u\n"), - ControlFile.checkPointCopy.oldestMulti); - printf(_("Latest checkpoint's oldestMulti's DB: %u\n"), - ControlFile.checkPointCopy.oldestMultiDB); - printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"), - ControlFile.checkPointCopy.oldestCommitTsXid); - printf(_("Latest checkpoint's newestCommitTsXid:%u\n"), - ControlFile.checkPointCopy.newestCommitTsXid); - printf(_("Maximum data alignment: %u\n"), - ControlFile.maxAlign); - /* we don't print floatFormat since can't say much useful about it */ - printf(_("Database block size: %u\n"), - ControlFile.blcksz); - printf(_("Blocks per segment of large relation: %u\n"), - ControlFile.relseg_size); - printf(_("Pages per SLRU segment: %u\n"), - ControlFile.slru_pages_per_segment); - printf(_("WAL block size: %u\n"), - ControlFile.xlog_blcksz); - printf(_("Bytes per WAL segment: %u\n"), - ControlFile.xlog_seg_size); - printf(_("Maximum length of identifiers: %u\n"), - ControlFile.nameDataLen); - printf(_("Maximum columns in an index: %u\n"), - ControlFile.indexMaxKeys); - printf(_("Maximum size of a TOAST chunk: %u\n"), - ControlFile.toast_max_chunk_size); - printf(_("Size of a large-object chunk: %u\n"), - ControlFile.loblksize); - /* This is no longer configurable, but users may still expect to see it: */ - printf(_("Date/time type storage: %s\n"), - _("64-bit integers")); - printf(_("Float8 argument passing: %s\n"), - (ControlFile.float8ByVal ? _("by value") : _("by reference"))); - printf(_("Data page checksum version: %u\n"), - ControlFile.data_checksum_version); - printf(_("Default char data signedness: %s\n"), - (ControlFile.default_char_signedness ? _("signed") : _("unsigned"))); + /* + * First, determine the maximum length of the description of all entries, + * some or all of which might be translated. + */ +#define CONTROLDATA_LINE(description, fmt, ...) \ + thislen = internal_wcswidth(_(description), \ + strlen(_(description)), \ + encoding); \ + if (thislen > maxlen) \ + maxlen = thislen; +#include "entries.h" +#undef CONTROLDATA_LINE + + /* + * Print each line: the possibly-translated description, then some padding + * spaces according to its display width, then the value. + */ +#define CONTROLDATA_LINE(description, fmt, ...) \ + { \ + int thisstrlen; \ + \ + thisstrlen = strlen(_(description)); \ + thislen = internal_wcswidth(_(description), \ + thisstrlen, \ + encoding); \ + printf("%s:%*s" fmt "\n", \ + _(description), \ + maxlen - thislen + 2, \ + " ", \ + __VA_ARGS__); \ + } +#include "entries.h" +#undef CONTROLDATA_LINE } @@ -1238,6 +1214,36 @@ usage(void) printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); } +/* + * Measure the display length of a single-line string in the given encoding. + * + * Similar to pg_wcswidth, written without dependency on libpq. + */ +static int +internal_wcswidth(const char *pwcs, size_t len, int encoding) +{ + int width = 0; + + while (len > 0) + { + int chlen, + chwidth; + + chlen = pg_encoding_mblen(encoding, pwcs); + if (len < (size_t) chlen) + break; /* Invalid string */ + + chwidth = pg_encoding_dsplen(encoding, pwcs); + if (chwidth > 0) + width += chwidth; + + pwcs += chlen; + len -= chlen; + } + return width; +} + + /* * strtouint32_strict -- like strtoul(), but returns uint32 and doesn't accept * negative values -- 2.47.3 --6po7oomtfoqj5cjo--