correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
The following documentation comment has been logged on the website: Page: https://www.postgresql.org/docs/17/auth-username-maps.html Description: Dear all, Pls. let me suggest the correction for the https://www.postgresql.org/docs/17/auth-username-maps.html page. It has the following sentence: ' If the database-username field starts with a slash (/), the remainder of the field is treated as a regular expression (see Section 9.7.3.1 for details of PostgreSQL's regular expression syntax). It is not possible to use \1 to use a capture from regular expression on system-username for a regular expression on database-username. ' It looks like 'to use a capture' has to be replaced by 'to capture'. best regards Alexey Shishkin
Re: correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
"David G. Johnston" writes: > Its good as far a narrow fix goes. > But how about the attached? More invasive but covers the salient points > better, IMO, and less repetitive than having the two fields have their own > basically copy-pasted paragraphs. Meh... I initially thought that merging the two paras sounded like a good idea, but I'm not finding that this formulation reads any better. Notably, as things stand we have parallel constructions "If the starts with an character" in the preceding para as well as these two, and I think it's good to keep that parallelism. I do agree that it's overly repetitive, but we could improve that by dropping the second instance of the parenthetical link to posix-syntax-details. > I didn't add an example but felt the point "be referenced a single time > within" to be needed since, usefulness not withstanding, writing \1\1 for > database-username works but only the first instance of \1 is replaced. Hmm, I wonder if that isn't a bug we should fix. It's hard to believe anyone is relying on the second \1 *not* getting replaced, and perhaps there are use-cases for multiple replacements. > Also, should we attempt to align this documentation and > pg_ident.conf.sample as pertains to pg-username vs. database-username? Agreed that making pg_ident.conf.sample match would be an improvement. regards, tom lane
Re: correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
"David G. Johnston" writes: > On Wed, Jul 9, 2025 at 7:56 AM PG Doc comments form > wrote: >> Pls. let me suggest the correction for the >> https://www.postgresql.org/docs/17/auth-username-maps.html page. >> It has the following sentence: >> ' >> If the database-username field starts with a slash (/), the remainder of >> the >> field is treated as a regular expression (see Section 9.7.3.1 for details >> of >> PostgreSQL's regular expression syntax). It is not possible to use \1 to >> use >> a capture from regular expression on system-username for a regular >> expression on database-username. >> ' >> It looks like 'to use a capture' has to be replaced by 'to capture'. >> best regards > What is written is factually correct. Your suggestion makes it > incorrect; and wouldn't be good English even if it was. The existing sentence is pretty mangled English, though. I think it would be clearer as When the database-username field is a regular expression, it is not possible to use \1 within it to refer to a capture from the system-username field. Thoughts? regards, tom lane
Outdated typedefs in documentation
Hi, I have noticed a slight mismatch between typedefs in docs and header files. On current master branch: - CustomScanState is missing custom_ps, pscan_len and slotOps fields in docs. - `methods` field of CustomPath, CustomScan and CustomScanState is missing `struct` in type. - BrinOpcInfo.oi_regular_nulls is missing. - pgNotify.next is missing. But the comment above it says apps should not use it, so I guess it can be left as is. Attached diff file shows other mismatches I could find. There are some comments that could be updated. Other differences are caused by indentation variations and false positives. The script I used for typedef extraction is also attached. Regards, Artem Fadeev. https://postgrespro.comdiff --git a/tmp/typedefs_output.code.txt b/tmp/typedefs_output.docs.txt index 25bee9e7846..e4e6c89fc90 100644 --- a/tmp/typedefs_output.code.txt +++ b/tmp/typedefs_output.docs.txt @@ -8,15 +8,11 @@ typedef struct OutputPluginCallbacks LogicalDecodeMessageCB message_cb; LogicalDecodeFilterByOriginCB filter_by_origin_cb; LogicalDecodeShutdownCB shutdown_cb; - - /* streaming of changes at prepare time */ LogicalDecodeFilterPrepareCB filter_prepare_cb; LogicalDecodeBeginPrepareCB begin_prepare_cb; LogicalDecodePrepareCB prepare_cb; LogicalDecodeCommitPreparedCB commit_prepared_cb; LogicalDecodeRollbackPreparedCB rollback_prepared_cb; - - /* streaming of changes */ LogicalDecodeStreamStartCB stream_start_cb; LogicalDecodeStreamStopCB stream_stop_cb; LogicalDecodeStreamAbortCB stream_abort_cb; @@ -43,11 +39,13 @@ typedef struct ArchiveModuleCallbacks === typedef struct { - float8 x, - y; + double x, y; } Point; === -typedef struct varlena text; +typedef struct { + int32 length; + char data[FLEXIBLE_ARRAY_MEMBER]; +} text; === typedef struct FuncCallContext { @@ -62,16 +60,16 @@ typedef struct FuncCallContext /* * OPTIONAL maximum number of calls * - * max_calls is here for convenience only and setting it is optional. If - * not set, you must provide alternative means to know when the function - * is done. + * max_calls is here for convenience only and setting it is optional. + * If not set, you must provide alternative means to know when the + * function is done. */ uint64 max_calls; /* * OPTIONAL pointer to miscellaneous user-provided context information * - * user_fctx is for use as a pointer to your own struct to retain + * user_fctx is for use as a pointer to your own data to retain * arbitrary context information between calls of your function. */ void *user_fctx; @@ -79,9 +77,10 @@ typedef struct FuncCallContext /* * OPTIONAL pointer to struct containing attribute type input metadata * - * attinmeta is for use when returning tuples (i.e. composite data types) - * and is not used when returning base data types. It is only needed if - * you intend to use BuildTupleFromCStrings() to create the return tuple. + * attinmeta is for use when returning tuples (i.e., composite data types) + * and is not used when returning base data types. It is only needed + * if you intend to use BuildTupleFromCStrings() to create the return + * tuple. */ AttInMetadata *attinmeta; @@ -90,15 +89,15 @@ typedef struct FuncCallContext * * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory - * context for any memory that is to be reused across multiple calls of - * the SRF. + * context for any memory that is to be reused across multiple calls + * of the SRF. */ MemoryContext multi_call_memory_ctx; /* * OPTIONAL pointer to struct containing tuple description * - * tuple_desc is for use when returning tuples (i.e. composite data types) + * tuple_desc is for use when returning tuples (i.e., composite data types) * and is only needed if you are going to build the tuples with * heap_form_tuple() rather than with BuildTupleFromCStrings(). Note that * the TupleDesc pointer stored here should usually have been run through @@ -122,34 +121,33 @@ typedef struct SPITupleTable SubTransactionId subid; /* subxact in which tuptable was created */ } SPITupleTable; === -typedef struct _PQconninfoOption +typedef struct { char *keyword; /* The keyword of the option */ char *envvar; /* Fallback environment variable name */ char *compiled; /* Fallback compiled in default value */ char *val; /* Option's current value, or NULL */ char *label; /* Label for field in connect dialog */ - char *dispchar; /* Indicates how to display this field in a - * connect dialog. Values are: "" Display - * entered value as is "*" Password field - - * hide value "D" Debug option - don't show - * by default */ + char *dispchar; /* Indicates how to display this field + in a connect dialog. Values are: + "" Display entered value as is + "*" Password field - hide value + "D" Debug option - don't show by default */ int dispsize; /* Field size in characters for dialog */
Re: correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
On Wed, Jul 9, 2025 at 7:56 AM PG Doc comments form wrote: > The following documentation comment has been logged on the website: > > Page: https://www.postgresql.org/docs/17/auth-username-maps.html > Description: > > Dear all, > Pls. let me suggest the correction for the > https://www.postgresql.org/docs/17/auth-username-maps.html page. > It has the following sentence: > ' > If the database-username field starts with a slash (/), the remainder of > the > field is treated as a regular expression (see Section 9.7.3.1 for details > of > PostgreSQL's regular expression syntax). It is not possible to use \1 to > use > a capture from regular expression on system-username for a regular > expression on database-username. > ' > It looks like 'to use a capture' has to be replaced by 'to capture'. > best regards > > What is written is factually correct. Your suggestion makes it incorrect; and wouldn't be good English even if it was. "to capture" involves (...) while using said capture involves \# David J.
Re: correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
On Wed, Jul 9, 2025 at 9:22 AM Tom Lane wrote: > "David G. Johnston" writes: > > On Wed, Jul 9, 2025 at 7:56 AM PG Doc comments form < > nore...@postgresql.org> > > wrote: > >> Pls. let me suggest the correction for the > >> https://www.postgresql.org/docs/17/auth-username-maps.html page. > >> It has the following sentence: > > The existing sentence is pretty mangled English, though. > Agreed. > When the database-username field is a regular expression, it is > not possible to use \1 within it to refer to a capture from > the system-username field. > > Thoughts? > Its good as far a narrow fix goes. But how about the attached? More invasive but covers the salient points better, IMO, and less repetitive than having the two fields have their own basically copy-pasted paragraphs. I didn't add an example but felt the point "be referenced a single time within" to be needed since, usefulness not withstanding, writing \1\1 for database-username works but only the first instance of \1 is replaced. Also, should we attempt to align this documentation and pg_ident.conf.sample as pertains to pg-username vs. database-username? David J. From 093ba8ec49060501c3930c45d9b3c1ba0669a536 Mon Sep 17 00:00:00 2001 From: "David G. Johnston" Date: Wed, 9 Jul 2025 11:30:07 -0700 Subject: [PATCH] doc: Reword pg_ident.conf explanation surrounding regexp Remove redundancy introduced by giving system-username and database-username their own paragraphs. This also allows a clean way to state that our behavior pertaining to capturing the first group in system-username regexp and referring to it in a non-regexp database-username. Emphasize this later requirement with an example and a warning. --- doc/src/sgml/client-auth.sgml | 44 ++- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 832b616a7bb..53bb2ef7ed3 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -999,37 +999,39 @@ local db1,db2,@demodbs all md5 + lose its special meaning. - If the system-username field starts with a slash (/), - the remainder of the field is treated as a regular expression. - (See for details of - PostgreSQL's regular expression syntax.) The regular - expression can include a single capture, or parenthesized subexpression, - which can then be referenced in the database-username - field as \1 (backslash-one). This allows the mapping of - multiple user names in a single line, which is particularly useful for - simple syntax substitutions. For example, these entries + Both system-username and database-username + can be specified using regular expressions by beginning the value with a slash + / (See for details of + PostgreSQL's regular expression syntax). + Of particular note is the capturing group and back reference feature; where + parentheses capture actual matched text and can be referred to using \m. + In the special case where the system-username + field is a regular expression with at least one capturing group, and, importantly, + the database-username field is not a regular expression, + the first capturing group in system-username can + be referenced a single time within the database-username + field using \1. For example, these first two entries mymap /^(.*)@mydomain\.com$ \1 mymap /^(.*)@otherdomain\.com$ guest +# mymap /^(.*)@example\.com$ /^\1-(example|other)$ # Invalid RegExp! will remove the domain part for users with system user names that end with @mydomain.com, and allow any user whose system name ends with @otherdomain.com to log in as guest. - Quoting a database-username containing + Note that quoting a database-username containing \1 does not make \1 lose its special meaning. - - If the database-username field starts with - a slash (/), the remainder of the field is treated - as a regular expression (see - for details of PostgreSQL's regular - expression syntax). It is not possible to use \1 - to use a capture from regular expression on - system-username for a regular expression - on database-username. - - + + +The commented-out third example above has an invalid regular expression which +will cause the pg_ident.conf file to fail to load. The problem is that within +a regular expression the \1 reference will always refer to +the context of the expression itself, and in this case at the point +\1 is used no capturing groups have been matched. + + Keep in mind that by default, a regular expression can match just part of -- 2.34.1
Re: correction suggestion for https://www.postgresql.org/docs/17/auth-username-maps.html
I wrote: > "David G. Johnston" writes: >> I didn't add an example but felt the point "be referenced a single time >> within" to be needed since, usefulness not withstanding, writing \1\1 for >> database-username works but only the first instance of \1 is replaced. > Hmm, I wonder if that isn't a bug we should fix. It's hard to believe > anyone is relying on the second \1 *not* getting replaced, and perhaps > there are use-cases for multiple replacements. Here's a quick patch for that. I hacked up 003_peer.pl enough to prove that multiple replacement works, but that test change is not committable as-is because it assumes that the "system user" name is "postgres". I don't like the existing test much either, since it only tests the case of the substituted string being empty, which means the substitution code could be quite broken and it wouldn't notice. But I don't offhand see a way to improve that without making assumptions about the incoming name... regards, tom lane diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 332fad27835..fecee8224d0 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -2873,8 +2873,11 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, !token_has_regexp(identLine->pg_user) && (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL) { + const char *repl_str; + size_t repl_len; + char *old_pg_user; char *expanded_pg_user; - int offset; + size_t offset; /* substitution of the first argument requested */ if (matches[1].rm_so < 0) @@ -2886,18 +2889,33 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, *error_p = true; return; } + repl_str = system_user + matches[1].rm_so; + repl_len = matches[1].rm_eo - matches[1].rm_so; /* - * length: original length minus length of \1 plus length of match - * plus null terminator + * It's allowed to have more than one \1 in the string, and we'll + * replace them all. But that's pretty unusual so we optimize on + * the assumption of only one occurrence, which motivates doing + * repeated replacements instead of making two passes over the + * string to determine the final length right away. */ - expanded_pg_user = palloc0(strlen(identLine->pg_user->string) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1); - offset = ofs - identLine->pg_user->string; - memcpy(expanded_pg_user, identLine->pg_user->string, offset); - memcpy(expanded_pg_user + offset, - system_user + matches[1].rm_so, - matches[1].rm_eo - matches[1].rm_so); - strcat(expanded_pg_user, ofs + 2); + old_pg_user = identLine->pg_user->string; + do + { +/* + * length: current length minus length of \1 plus length of + * replacement plus null terminator + */ +expanded_pg_user = palloc(strlen(old_pg_user) - 2 + repl_len + 1); +/* ofs points into the old_pg_user string at this point */ +offset = ofs - old_pg_user; +memcpy(expanded_pg_user, old_pg_user, offset); +memcpy(expanded_pg_user + offset, repl_str, repl_len); +strcpy(expanded_pg_user + offset + repl_len, ofs + 2); +if (old_pg_user != identLine->pg_user->string) + pfree(old_pg_user); +old_pg_user = expanded_pg_user; + } while ((ofs = strstr(old_pg_user + offset + repl_len, "\\1")) != NULL); /* * Mark the token as quoted, so it will only be compared literally diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl index f2320b62c87..8a9431e5594 100644 --- a/src/test/authentication/t/003_peer.pl +++ b/src/test/authentication/t/003_peer.pl @@ -93,6 +93,8 @@ if ($node->log_contains( $node->safe_psql('postgres', qq{CREATE ROLE testmapuser LOGIN}); $node->safe_psql('postgres', "CREATE ROLE testmapgroup NOLOGIN"); $node->safe_psql('postgres', "GRANT testmapgroup TO testmapuser"); +# This role is for testing \1 substitution. +$node->safe_psql('postgres', qq{CREATE ROLE testgresgresmapuser LOGIN}); # Note the double quotes here. $node->safe_psql('postgres', 'CREATE ROLE "testmapgroupliteral\\1" LOGIN'); $node->safe_psql('postgres', 'GRANT "testmapgroupliteral\\1" TO testmapuser'); @@ -212,10 +214,10 @@ test_role( # Success as the regular expression matches and \1 is replaced in the given # subexpression. -reset_pg_ident($node, 'mypeermap', qq{/^$system_user(.*)\$}, 'test\1mapuser'); +reset_pg_ident($node, 'mypeermap', qq{/^post(.*)\$}, 'test\1\1mapuser'); test_role( $node, - qq{testmapuser}, + qq{testgresgresmapuser}, 'peer', 0, 'with regular expression in user name map with \1 replaced',