On Tue, Mar 22, 2022 at 09:38:00PM +0800, Julien Rouhaud wrote:
> On Tue, Mar 22, 2022 at 03:21:20PM +0300, Aleksander Alekseev wrote:
>> Since v3-0002 adds a new view and alters pg_proc.dat shouldn't it also
>> increase CATALOG_VERSION_NO? Not sure if we generally do this in the
>> patches or expect the committer to make the change manually.
> 
> It's better to no include any catversion bump, otherwise the patches will
> rot very fast.  Author can mention the need for a catversion bump in the
> commit message, and I usually do so but I apparently forgot.

Yeah, committers take care of that.  You would just expose yourself to
more noise in the CF bot for no gain, as a catversion bump is useful
after a patch has been merged so as as users are able to know when a
cluster needs to be pg_upgrade'd or initdb'd because the catalog
created and run are incompatible.

>> All in all, the patchset seems to be in good shape and I don't have
>> anything but some little nitpicks. It passes `make installcheck` and I
>> verified manually that the file inclusion 1) works 2) write proper error
>> messages to the logfile when the included file doesn't exist or has wrong
>> permissions.
> 
> Thanks!

Pushing forward with 0001 by the end of the CF is the part that has no
controversy IMO, and I have no objections to it.  Now, after looking
at this part, I found a few things, as of:
- HbaToken, the set of elements in the lists of TokenizedAuthLine, is
a weird to use as this layer gets used by both pg_hba.conf and
pg_indent.conf before transforming them into each HbaLine and
IdentLine.  While making this part of the internals exposed, I think
that we'd better rename that to AuthToken at least.  This impacts the
names of some routines internal to hba.c to copy and create
AuthTokens.
- s/gethba_options/get_hba_options/, to be consistent with
fill_hba_view() and other things.
- The comment at the top of tokenize_auth_file() needed a refresh.

That's mostly cosmetic, and the rest of the code moved is identical.
So at the end this part looks rather commitable to me.

I have not been able to test 0002 in details, but it looks rather
rather sane to me at quick glance, and it is simple.  The argument
about more TAP tests applies to it, though, even if there is one SQL
test to check the function execution.  It is probably better to not
consider 0003 and 0004 for this CF.
--
Michael
From dbee2a5b673b3bac2e4df0ec966912c29416fc2c Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Wed, 23 Mar 2022 10:56:15 +0900
Subject: [PATCH v4] Extract view processing code from hba.c

This file is already quite big and a following commit will add yet an
additional view, so let's move all the view related code in hba.c into a new
adt/hbafuncs.c.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/include/libpq/hba.h          |  31 ++
 src/backend/libpq/hba.c          | 517 +++----------------------------
 src/backend/utils/adt/Makefile   |   1 +
 src/backend/utils/adt/hbafuncs.c | 428 +++++++++++++++++++++++++
 src/tools/pgindent/typedefs.list |   4 +-
 5 files changed, 511 insertions(+), 470 deletions(-)
 create mode 100644 src/backend/utils/adt/hbafuncs.c

diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 8d9f3821b1..13ecb329f8 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -132,6 +132,34 @@ typedef struct IdentLine
 	regex_t		re;
 } IdentLine;
 
+/*
+ * A single string token lexed from an authentication configuration file
+ * (pg_ident.conf or pg_hba.conf), together with whether the token has
+ * been quoted.
+ */
+typedef struct AuthToken
+{
+	char	   *string;
+	bool		quoted;
+} AuthToken;
+
+/*
+ * TokenizedAuthLine represents one line lexed from an authentication
+ * configuration file.  Each item in the "fields" list is a sub-list of
+ * AuthTokens.  We don't emit a TokenizedAuthLine for empty or all-comment
+ * lines, so "fields" is never NIL (nor are any of its sub-lists).
+ *
+ * Exception: if an error occurs during tokenization, we might have
+ * fields == NIL, in which case err_msg != NULL.
+ */
+typedef struct TokenizedAuthLine
+{
+	List	   *fields;			/* List of lists of AuthTokens */
+	int			line_num;		/* Line number */
+	char	   *raw_line;		/* Raw line text */
+	char	   *err_msg;		/* Error message if any */
+} TokenizedAuthLine;
+
 /* kluge to avoid including libpq/libpq-be.h here */
 typedef struct Port hbaPort;
 
@@ -142,6 +170,9 @@ extern void hba_getauthmethod(hbaPort *port);
 extern int	check_usermap(const char *usermap_name,
 						  const char *pg_role, const char *auth_user,
 						  bool case_sensitive);
+extern HbaLine *parse_hba_line(TokenizedAuthLine *tok_line, int elevel);
 extern bool pg_isblank(const char c);
+extern MemoryContext tokenize_auth_file(const char *filename, FILE *file,
+										List **tok_lines, int elevel);
 
 #endif							/* HBA_H */
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 90953c38f3..673135144d 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -68,32 +68,6 @@ typedef struct check_network_data
 #define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
 #define token_matches(t, k)  (strcmp(t->string, k) == 0)
 
-/*
- * A single string token lexed from a config file, together with whether
- * the token had been quoted.
- */
-typedef struct HbaToken
-{
-	char	   *string;
-	bool		quoted;
-} HbaToken;
-
-/*
- * TokenizedLine represents one line lexed from a config file.
- * Each item in the "fields" list is a sub-list of HbaTokens.
- * We don't emit a TokenizedLine for empty or all-comment lines,
- * so "fields" is never NIL (nor are any of its sub-lists).
- * Exception: if an error occurs during tokenization, we might
- * have fields == NIL, in which case err_msg != NULL.
- */
-typedef struct TokenizedLine
-{
-	List	   *fields;			/* List of lists of HbaTokens */
-	int			line_num;		/* Line number */
-	char	   *raw_line;		/* Raw line text */
-	char	   *err_msg;		/* Error message if any */
-} TokenizedLine;
-
 /*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
@@ -138,16 +112,10 @@ static const char *const UserAuthName[] =
 };
 
 
-static MemoryContext tokenize_file(const char *filename, FILE *file,
-								   List **tok_lines, int elevel);
 static List *tokenize_inc_file(List *tokens, const char *outer_filename,
 							   const char *inc_filename, int elevel, char **err_msg);
 static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
 							   int elevel, char **err_msg);
-static ArrayType *gethba_options(HbaLine *hba);
-static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
-						  int lineno, HbaLine *hba, const char *err_msg);
-static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
 
 
 /*
@@ -288,31 +256,31 @@ next_token(char **lineptr, char *buf, int bufsz,
 }
 
 /*
- * Construct a palloc'd HbaToken struct, copying the given string.
+ * Construct a palloc'd AuthToken struct, copying the given string.
  */
-static HbaToken *
-make_hba_token(const char *token, bool quoted)
+static AuthToken *
+make_auth_token(const char *token, bool quoted)
 {
-	HbaToken   *hbatoken;
+	AuthToken  *authtoken;
 	int			toklen;
 
 	toklen = strlen(token);
 	/* we copy string into same palloc block as the struct */
-	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
-	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
-	hbatoken->quoted = quoted;
-	memcpy(hbatoken->string, token, toklen + 1);
+	authtoken = (AuthToken *) palloc(sizeof(AuthToken) + toklen + 1);
+	authtoken->string = (char *) authtoken + sizeof(AuthToken);
+	authtoken->quoted = quoted;
+	memcpy(authtoken->string, token, toklen + 1);
 
-	return hbatoken;
+	return authtoken;
 }
 
 /*
- * Copy a HbaToken struct into freshly palloc'd memory.
+ * Copy a AuthToken struct into freshly palloc'd memory.
  */
-static HbaToken *
-copy_hba_token(HbaToken *in)
+static AuthToken *
+copy_auth_token(AuthToken *in)
 {
-	HbaToken   *out = make_hba_token(in->string, in->quoted);
+	AuthToken  *out = make_auth_token(in->string, in->quoted);
 
 	return out;
 }
@@ -329,7 +297,7 @@ copy_hba_token(HbaToken *in)
  * may be non-NIL anyway, so *err_msg must be tested to determine whether
  * there was an error.
  *
- * The result is a List of HbaToken structs, one for each token in the field,
+ * The result is a List of AuthToken structs, one for each token in the field,
  * or NIL if we reached EOL.
  */
 static List *
@@ -353,7 +321,7 @@ next_field_expand(const char *filename, char **lineptr,
 			tokens = tokenize_inc_file(tokens, filename, buf + 1,
 									   elevel, err_msg);
 		else
-			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
+			tokens = lappend(tokens, make_auth_token(buf, initial_quote));
 	} while (trailing_comma && (*err_msg == NULL));
 
 	return tokens;
@@ -364,7 +332,7 @@ next_field_expand(const char *filename, char **lineptr,
  *		Expand a file included from another file into an hba "field"
  *
  * Opens and tokenises a file included from another HBA config file with @,
- * and returns all values found therein as a flat list of HbaTokens.  If a
+ * and returns all values found therein as a flat list of AuthTokens.  If a
  * @-token is found, recursively expand it.  The newly read tokens are
  * appended to "tokens" (so that foo,bar,@baz does what you expect).
  * All new tokens are allocated in caller's memory context.
@@ -419,7 +387,7 @@ tokenize_inc_file(List *tokens,
 	}
 
 	/* There is possible recursion here if the file contains @ */
-	linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, elevel);
+	linecxt = tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel);
 
 	FreeFile(inc_file);
 	pfree(inc_fullname);
@@ -427,7 +395,7 @@ tokenize_inc_file(List *tokens,
 	/* Copy all tokens found in the file and append to the tokens list */
 	foreach(inc_line, inc_lines)
 	{
-		TokenizedLine *tok_line = (TokenizedLine *) lfirst(inc_line);
+		TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(inc_line);
 		ListCell   *inc_field;
 
 		/* If any line has an error, propagate that up to caller */
@@ -444,9 +412,9 @@ tokenize_inc_file(List *tokens,
 
 			foreach(inc_token, inc_tokens)
 			{
-				HbaToken   *token = lfirst(inc_token);
+				AuthToken  *token = lfirst(inc_token);
 
-				tokens = lappend(tokens, copy_hba_token(token));
+				tokens = lappend(tokens, copy_auth_token(token));
 			}
 		}
 	}
@@ -456,9 +424,11 @@ tokenize_inc_file(List *tokens,
 }
 
 /*
- * Tokenize the given file.
+ * tokenize_auth_file
+ *		Tokenize the given file.
  *
- * The output is a list of TokenizedLine structs; see struct definition above.
+ * The output is a list of TokenizedAuthLine structs; see the struct definition
+ * in libpq/hba.h.
  *
  * filename: the absolute path to the target file
  * file: the already-opened target file
@@ -466,14 +436,15 @@ tokenize_inc_file(List *tokens,
  * elevel: message logging level
  *
  * Errors are reported by logging messages at ereport level elevel and by
- * adding TokenizedLine structs containing non-null err_msg fields to the
+ * adding TokenizedAuthLine structs containing non-null err_msg fields to the
  * output list.
  *
  * Return value is a memory context which contains all memory allocated by
  * this function (it's a child of caller's context).
  */
-static MemoryContext
-tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
+MemoryContext
+tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
+				   int elevel)
 {
 	int			line_number = 1;
 	StringInfoData buf;
@@ -481,7 +452,7 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
 	MemoryContext oldcxt;
 
 	linecxt = AllocSetContextCreate(CurrentMemoryContext,
-									"tokenize_file",
+									"tokenize_auth_file",
 									ALLOCSET_SMALL_SIZES);
 	oldcxt = MemoryContextSwitchTo(linecxt);
 
@@ -550,12 +521,14 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
 				current_line = lappend(current_line, current_field);
 		}
 
-		/* Reached EOL; emit line to TokenizedLine list unless it's boring */
+		/*
+		 * Reached EOL; emit line to TokenizedAuthLine list unless it's boring
+		 */
 		if (current_line != NIL || err_msg != NULL)
 		{
-			TokenizedLine *tok_line;
+			TokenizedAuthLine *tok_line;
 
-			tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine));
+			tok_line = (TokenizedAuthLine *) palloc(sizeof(TokenizedAuthLine));
 			tok_line->fields = current_line;
 			tok_line->line_num = line_number;
 			tok_line->raw_line = pstrdup(buf.data);
@@ -600,13 +573,13 @@ is_member(Oid userid, const char *role)
 }
 
 /*
- * Check HbaToken list for a match to role, allowing group names.
+ * Check AuthToken list for a match to role, allowing group names.
  */
 static bool
 check_role(const char *role, Oid roleid, List *tokens)
 {
 	ListCell   *cell;
-	HbaToken   *tok;
+	AuthToken  *tok;
 
 	foreach(cell, tokens)
 	{
@@ -624,13 +597,13 @@ check_role(const char *role, Oid roleid, List *tokens)
 }
 
 /*
- * Check to see if db/role combination matches HbaToken list.
+ * Check to see if db/role combination matches AuthToken list.
  */
 static bool
 check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
 {
 	ListCell   *cell;
-	HbaToken   *tok;
+	AuthToken  *tok;
 
 	foreach(cell, tokens)
 	{
@@ -962,8 +935,8 @@ do { \
  * to have set a memory context that will be reset if this function returns
  * NULL.
  */
-static HbaLine *
-parse_hba_line(TokenizedLine *tok_line, int elevel)
+HbaLine *
+parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
 {
 	int			line_num = tok_line->line_num;
 	char	  **err_msg = &tok_line->err_msg;
@@ -976,7 +949,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 	ListCell   *field;
 	List	   *tokens;
 	ListCell   *tokencell;
-	HbaToken   *token;
+	AuthToken  *token;
 	HbaLine    *parsedline;
 
 	parsedline = palloc0(sizeof(HbaLine));
@@ -1097,7 +1070,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 	foreach(tokencell, tokens)
 	{
 		parsedline->databases = lappend(parsedline->databases,
-										copy_hba_token(lfirst(tokencell)));
+										copy_auth_token(lfirst(tokencell)));
 	}
 
 	/* Get the roles. */
@@ -1117,7 +1090,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 	foreach(tokencell, tokens)
 	{
 		parsedline->roles = lappend(parsedline->roles,
-									copy_hba_token(lfirst(tokencell)));
+									copy_auth_token(lfirst(tokencell)));
 	}
 
 	if (parsedline->conntype != ctLocal)
@@ -2257,7 +2230,7 @@ load_hba(void)
 		return false;
 	}
 
-	linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG);
+	linecxt = tokenize_auth_file(HbaFileName, file, &hba_lines, LOG);
 	FreeFile(file);
 
 	/* Now parse all the lines */
@@ -2268,7 +2241,7 @@ load_hba(void)
 	oldcxt = MemoryContextSwitchTo(hbacxt);
 	foreach(line, hba_lines)
 	{
-		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
+		TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
 		HbaLine    *newline;
 
 		/* don't parse lines that already have errors */
@@ -2328,398 +2301,6 @@ load_hba(void)
 	return true;
 }
 
-/*
- * This macro specifies the maximum number of authentication options
- * that are possible with any given authentication method that is supported.
- * Currently LDAP supports 11, and there are 3 that are not dependent on
- * the auth method here.  It may not actually be possible to set all of them
- * at the same time, but we'll set the macro value high enough to be
- * conservative and avoid warnings from static analysis tools.
- */
-#define MAX_HBA_OPTIONS 14
-
-/*
- * Create a text array listing the options specified in the HBA line.
- * Return NULL if no options are specified.
- */
-static ArrayType *
-gethba_options(HbaLine *hba)
-{
-	int			noptions;
-	Datum		options[MAX_HBA_OPTIONS];
-
-	noptions = 0;
-
-	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
-	{
-		if (hba->include_realm)
-			options[noptions++] =
-				CStringGetTextDatum("include_realm=true");
-
-		if (hba->krb_realm)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm));
-	}
-
-	if (hba->usermap)
-		options[noptions++] =
-			CStringGetTextDatum(psprintf("map=%s", hba->usermap));
-
-	if (hba->clientcert != clientCertOff)
-		options[noptions++] =
-			CStringGetTextDatum(psprintf("clientcert=%s", (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full"));
-
-	if (hba->pamservice)
-		options[noptions++] =
-			CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice));
-
-	if (hba->auth_method == uaLDAP)
-	{
-		if (hba->ldapserver)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver));
-
-		if (hba->ldapport)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport));
-
-		if (hba->ldaptls)
-			options[noptions++] =
-				CStringGetTextDatum("ldaptls=true");
-
-		if (hba->ldapprefix)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix));
-
-		if (hba->ldapsuffix)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix));
-
-		if (hba->ldapbasedn)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn));
-
-		if (hba->ldapbinddn)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn));
-
-		if (hba->ldapbindpasswd)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapbindpasswd=%s",
-											 hba->ldapbindpasswd));
-
-		if (hba->ldapsearchattribute)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapsearchattribute=%s",
-											 hba->ldapsearchattribute));
-
-		if (hba->ldapsearchfilter)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapsearchfilter=%s",
-											 hba->ldapsearchfilter));
-
-		if (hba->ldapscope)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
-	}
-
-	if (hba->auth_method == uaRADIUS)
-	{
-		if (hba->radiusservers_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
-
-		if (hba->radiussecrets_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
-
-		if (hba->radiusidentifiers_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
-
-		if (hba->radiusports_s)
-			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
-	}
-
-	/* If you add more options, consider increasing MAX_HBA_OPTIONS. */
-	Assert(noptions <= MAX_HBA_OPTIONS);
-
-	if (noptions > 0)
-		return construct_array(options, noptions, TEXTOID, -1, false, TYPALIGN_INT);
-	else
-		return NULL;
-}
-
-/* Number of columns in pg_hba_file_rules view */
-#define NUM_PG_HBA_FILE_RULES_ATTS	 9
-
-/*
- * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore
- *
- * tuple_store: where to store data
- * tupdesc: tuple descriptor for the view
- * lineno: pg_hba.conf line number (must always be valid)
- * hba: parsed line data (can be NULL, in which case err_msg should be set)
- * err_msg: error message (NULL if none)
- *
- * Note: leaks memory, but we don't care since this is run in a short-lived
- * memory context.
- */
-static void
-fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
-			  int lineno, HbaLine *hba, const char *err_msg)
-{
-	Datum		values[NUM_PG_HBA_FILE_RULES_ATTS];
-	bool		nulls[NUM_PG_HBA_FILE_RULES_ATTS];
-	char		buffer[NI_MAXHOST];
-	HeapTuple	tuple;
-	int			index;
-	ListCell   *lc;
-	const char *typestr;
-	const char *addrstr;
-	const char *maskstr;
-	ArrayType  *options;
-
-	Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS);
-
-	memset(values, 0, sizeof(values));
-	memset(nulls, 0, sizeof(nulls));
-	index = 0;
-
-	/* line_number */
-	values[index++] = Int32GetDatum(lineno);
-
-	if (hba != NULL)
-	{
-		/* type */
-		/* Avoid a default: case so compiler will warn about missing cases */
-		typestr = NULL;
-		switch (hba->conntype)
-		{
-			case ctLocal:
-				typestr = "local";
-				break;
-			case ctHost:
-				typestr = "host";
-				break;
-			case ctHostSSL:
-				typestr = "hostssl";
-				break;
-			case ctHostNoSSL:
-				typestr = "hostnossl";
-				break;
-			case ctHostGSS:
-				typestr = "hostgssenc";
-				break;
-			case ctHostNoGSS:
-				typestr = "hostnogssenc";
-				break;
-		}
-		if (typestr)
-			values[index++] = CStringGetTextDatum(typestr);
-		else
-			nulls[index++] = true;
-
-		/* database */
-		if (hba->databases)
-		{
-			/*
-			 * Flatten HbaToken list to string list.  It might seem that we
-			 * should re-quote any quoted tokens, but that has been rejected
-			 * on the grounds that it makes it harder to compare the array
-			 * elements to other system catalogs.  That makes entries like
-			 * "all" or "samerole" formally ambiguous ... but users who name
-			 * databases/roles that way are inflicting their own pain.
-			 */
-			List	   *names = NIL;
-
-			foreach(lc, hba->databases)
-			{
-				HbaToken   *tok = lfirst(lc);
-
-				names = lappend(names, tok->string);
-			}
-			values[index++] = PointerGetDatum(strlist_to_textarray(names));
-		}
-		else
-			nulls[index++] = true;
-
-		/* user */
-		if (hba->roles)
-		{
-			/* Flatten HbaToken list to string list; see comment above */
-			List	   *roles = NIL;
-
-			foreach(lc, hba->roles)
-			{
-				HbaToken   *tok = lfirst(lc);
-
-				roles = lappend(roles, tok->string);
-			}
-			values[index++] = PointerGetDatum(strlist_to_textarray(roles));
-		}
-		else
-			nulls[index++] = true;
-
-		/* address and netmask */
-		/* Avoid a default: case so compiler will warn about missing cases */
-		addrstr = maskstr = NULL;
-		switch (hba->ip_cmp_method)
-		{
-			case ipCmpMask:
-				if (hba->hostname)
-				{
-					addrstr = hba->hostname;
-				}
-				else
-				{
-					/*
-					 * Note: if pg_getnameinfo_all fails, it'll set buffer to
-					 * "???", which we want to return.
-					 */
-					if (hba->addrlen > 0)
-					{
-						if (pg_getnameinfo_all(&hba->addr, hba->addrlen,
-											   buffer, sizeof(buffer),
-											   NULL, 0,
-											   NI_NUMERICHOST) == 0)
-							clean_ipv6_addr(hba->addr.ss_family, buffer);
-						addrstr = pstrdup(buffer);
-					}
-					if (hba->masklen > 0)
-					{
-						if (pg_getnameinfo_all(&hba->mask, hba->masklen,
-											   buffer, sizeof(buffer),
-											   NULL, 0,
-											   NI_NUMERICHOST) == 0)
-							clean_ipv6_addr(hba->mask.ss_family, buffer);
-						maskstr = pstrdup(buffer);
-					}
-				}
-				break;
-			case ipCmpAll:
-				addrstr = "all";
-				break;
-			case ipCmpSameHost:
-				addrstr = "samehost";
-				break;
-			case ipCmpSameNet:
-				addrstr = "samenet";
-				break;
-		}
-		if (addrstr)
-			values[index++] = CStringGetTextDatum(addrstr);
-		else
-			nulls[index++] = true;
-		if (maskstr)
-			values[index++] = CStringGetTextDatum(maskstr);
-		else
-			nulls[index++] = true;
-
-		/* auth_method */
-		values[index++] = CStringGetTextDatum(hba_authname(hba->auth_method));
-
-		/* options */
-		options = gethba_options(hba);
-		if (options)
-			values[index++] = PointerGetDatum(options);
-		else
-			nulls[index++] = true;
-	}
-	else
-	{
-		/* no parsing result, so set relevant fields to nulls */
-		memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool));
-	}
-
-	/* error */
-	if (err_msg)
-		values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg);
-	else
-		nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true;
-
-	tuple = heap_form_tuple(tupdesc, values, nulls);
-	tuplestore_puttuple(tuple_store, tuple);
-}
-
-/*
- * Read the pg_hba.conf file and fill the tuplestore with view records.
- */
-static void
-fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
-{
-	FILE	   *file;
-	List	   *hba_lines = NIL;
-	ListCell   *line;
-	MemoryContext linecxt;
-	MemoryContext hbacxt;
-	MemoryContext oldcxt;
-
-	/*
-	 * In the unlikely event that we can't open pg_hba.conf, we throw an
-	 * error, rather than trying to report it via some sort of view entry.
-	 * (Most other error conditions should result in a message in a view
-	 * entry.)
-	 */
-	file = AllocateFile(HbaFileName, "r");
-	if (file == NULL)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not open configuration file \"%s\": %m",
-						HbaFileName)));
-
-	linecxt = tokenize_file(HbaFileName, file, &hba_lines, DEBUG3);
-	FreeFile(file);
-
-	/* Now parse all the lines */
-	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
-								   "hba parser context",
-								   ALLOCSET_SMALL_SIZES);
-	oldcxt = MemoryContextSwitchTo(hbacxt);
-	foreach(line, hba_lines)
-	{
-		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
-		HbaLine    *hbaline = NULL;
-
-		/* don't parse lines that already have errors */
-		if (tok_line->err_msg == NULL)
-			hbaline = parse_hba_line(tok_line, DEBUG3);
-
-		fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
-					  hbaline, tok_line->err_msg);
-	}
-
-	/* Free tokenizer memory */
-	MemoryContextDelete(linecxt);
-	/* Free parse_hba_line memory */
-	MemoryContextSwitchTo(oldcxt);
-	MemoryContextDelete(hbacxt);
-}
-
-/*
- * SQL-accessible SRF to return all the entries in the pg_hba.conf file.
- */
-Datum
-pg_hba_file_rules(PG_FUNCTION_ARGS)
-{
-	ReturnSetInfo *rsi;
-
-	/*
-	 * Build tuplestore to hold the result rows.  We must use the Materialize
-	 * mode to be safe against HBA file changes while the cursor is open.
-	 * It's also more efficient than having to look up our current position in
-	 * the parsed list every time.
-	 */
-	SetSingleFuncCall(fcinfo, 0);
-
-	/* Fill the tuplestore */
-	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-	fill_hba_view(rsi->setResult, rsi->setDesc);
-
-	PG_RETURN_NULL();
-}
-
 
 /*
  * Parse one tokenised line from the ident config file and store the result in
@@ -2735,12 +2316,12 @@ pg_hba_file_rules(PG_FUNCTION_ARGS)
  * NULL.
  */
 static IdentLine *
-parse_ident_line(TokenizedLine *tok_line)
+parse_ident_line(TokenizedAuthLine *tok_line)
 {
 	int			line_num = tok_line->line_num;
 	ListCell   *field;
 	List	   *tokens;
-	HbaToken   *token;
+	AuthToken  *token;
 	IdentLine  *parsedline;
 
 	Assert(tok_line->fields != NIL);
@@ -3026,7 +2607,7 @@ load_ident(void)
 		return false;
 	}
 
-	linecxt = tokenize_file(IdentFileName, file, &ident_lines, LOG);
+	linecxt = tokenize_auth_file(IdentFileName, file, &ident_lines, LOG);
 	FreeFile(file);
 
 	/* Now parse all the lines */
@@ -3037,7 +2618,7 @@ load_ident(void)
 	oldcxt = MemoryContextSwitchTo(ident_context);
 	foreach(line_cell, ident_lines)
 	{
-		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell);
+		TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line_cell);
 
 		/* don't parse lines that already have errors */
 		if (tok_line->err_msg != NULL)
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 41b486bcef..7c722ea2ce 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -42,6 +42,7 @@ OBJS = \
 	geo_ops.o \
 	geo_selfuncs.o \
 	geo_spgist.o \
+	hbafuncs.o \
 	inet_cidr_ntop.o \
 	inet_net_pton.o \
 	int.o \
diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c
new file mode 100644
index 0000000000..f46cd935a1
--- /dev/null
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -0,0 +1,428 @@
+/*-------------------------------------------------------------------------
+ *
+ * hbafuncs.c
+ *	  Support functions for SQL views of authentication files.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/hbafuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/objectaddress.h"
+#include "common/ip.h"
+#include "funcapi.h"
+#include "libpq/hba.h"
+#include "miscadmin.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+
+
+static ArrayType *get_hba_options(HbaLine *hba);
+static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
+						  int lineno, HbaLine *hba, const char *err_msg);
+static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
+
+
+/*
+ * This macro specifies the maximum number of authentication options
+ * that are possible with any given authentication method that is supported.
+ * Currently LDAP supports 11, and there are 3 that are not dependent on
+ * the auth method here.  It may not actually be possible to set all of them
+ * at the same time, but we'll set the macro value high enough to be
+ * conservative and avoid warnings from static analysis tools.
+ */
+#define MAX_HBA_OPTIONS 14
+
+/*
+ * Create a text array listing the options specified in the HBA line.
+ * Return NULL if no options are specified.
+ */
+static ArrayType *
+get_hba_options(HbaLine *hba)
+{
+	int			noptions;
+	Datum		options[MAX_HBA_OPTIONS];
+
+	noptions = 0;
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+			options[noptions++] =
+				CStringGetTextDatum("include_realm=true");
+
+		if (hba->krb_realm)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm));
+	}
+
+	if (hba->usermap)
+		options[noptions++] =
+			CStringGetTextDatum(psprintf("map=%s", hba->usermap));
+
+	if (hba->clientcert != clientCertOff)
+		options[noptions++] =
+			CStringGetTextDatum(psprintf("clientcert=%s", (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full"));
+
+	if (hba->pamservice)
+		options[noptions++] =
+			CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice));
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver));
+
+		if (hba->ldapport)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport));
+
+		if (hba->ldaptls)
+			options[noptions++] =
+				CStringGetTextDatum("ldaptls=true");
+
+		if (hba->ldapprefix)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix));
+
+		if (hba->ldapsuffix)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix));
+
+		if (hba->ldapbasedn)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn));
+
+		if (hba->ldapbinddn)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn));
+
+		if (hba->ldapbindpasswd)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapbindpasswd=%s",
+											 hba->ldapbindpasswd));
+
+		if (hba->ldapsearchattribute)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapsearchattribute=%s",
+											 hba->ldapsearchattribute));
+
+		if (hba->ldapsearchfilter)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapsearchfilter=%s",
+											 hba->ldapsearchfilter));
+
+		if (hba->ldapscope)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusservers_s)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
+
+		if (hba->radiussecrets_s)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
+
+		if (hba->radiusidentifiers_s)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
+
+		if (hba->radiusports_s)
+			options[noptions++] =
+				CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
+	}
+
+	/* If you add more options, consider increasing MAX_HBA_OPTIONS. */
+	Assert(noptions <= MAX_HBA_OPTIONS);
+
+	if (noptions > 0)
+		return construct_array(options, noptions, TEXTOID, -1, false, TYPALIGN_INT);
+	else
+		return NULL;
+}
+
+/* Number of columns in pg_hba_file_rules view */
+#define NUM_PG_HBA_FILE_RULES_ATTS	 9
+
+/*
+ * fill_hba_line
+ *		Build one row of pg_hba_file_rules view, add it to tuplestore.
+ *
+ * tuple_store: where to store data
+ * tupdesc: tuple descriptor for the view
+ * lineno: pg_hba.conf line number (must always be valid)
+ * hba: parsed line data (can be NULL, in which case err_msg should be set)
+ * err_msg: error message (NULL if none)
+ *
+ * Note: leaks memory, but we don't care since this is run in a short-lived
+ * memory context.
+ */
+static void
+fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
+			  int lineno, HbaLine *hba, const char *err_msg)
+{
+	Datum		values[NUM_PG_HBA_FILE_RULES_ATTS];
+	bool		nulls[NUM_PG_HBA_FILE_RULES_ATTS];
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	ListCell   *lc;
+	const char *typestr;
+	const char *addrstr;
+	const char *maskstr;
+	ArrayType  *options;
+
+	Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS);
+
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+	index = 0;
+
+	/* line_number */
+	values[index++] = Int32GetDatum(lineno);
+
+	if (hba != NULL)
+	{
+		/* type */
+		/* Avoid a default: case so compiler will warn about missing cases */
+		typestr = NULL;
+		switch (hba->conntype)
+		{
+			case ctLocal:
+				typestr = "local";
+				break;
+			case ctHost:
+				typestr = "host";
+				break;
+			case ctHostSSL:
+				typestr = "hostssl";
+				break;
+			case ctHostNoSSL:
+				typestr = "hostnossl";
+				break;
+			case ctHostGSS:
+				typestr = "hostgssenc";
+				break;
+			case ctHostNoGSS:
+				typestr = "hostnogssenc";
+				break;
+		}
+		if (typestr)
+			values[index++] = CStringGetTextDatum(typestr);
+		else
+			nulls[index++] = true;
+
+		/* database */
+		if (hba->databases)
+		{
+			/*
+			 * Flatten AuthToken list to string list.  It might seem that we
+			 * should re-quote any quoted tokens, but that has been rejected
+			 * on the grounds that it makes it harder to compare the array
+			 * elements to other system catalogs.  That makes entries like
+			 * "all" or "samerole" formally ambiguous ... but users who name
+			 * databases/roles that way are inflicting their own pain.
+			 */
+			List	   *names = NIL;
+
+			foreach(lc, hba->databases)
+			{
+				AuthToken  *tok = lfirst(lc);
+
+				names = lappend(names, tok->string);
+			}
+			values[index++] = PointerGetDatum(strlist_to_textarray(names));
+		}
+		else
+			nulls[index++] = true;
+
+		/* user */
+		if (hba->roles)
+		{
+			/* Flatten AuthToken list to string list; see comment above */
+			List	   *roles = NIL;
+
+			foreach(lc, hba->roles)
+			{
+				AuthToken  *tok = lfirst(lc);
+
+				roles = lappend(roles, tok->string);
+			}
+			values[index++] = PointerGetDatum(strlist_to_textarray(roles));
+		}
+		else
+			nulls[index++] = true;
+
+		/* address and netmask */
+		/* Avoid a default: case so compiler will warn about missing cases */
+		addrstr = maskstr = NULL;
+		switch (hba->ip_cmp_method)
+		{
+			case ipCmpMask:
+				if (hba->hostname)
+				{
+					addrstr = hba->hostname;
+				}
+				else
+				{
+					/*
+					 * Note: if pg_getnameinfo_all fails, it'll set buffer to
+					 * "???", which we want to return.
+					 */
+					if (hba->addrlen > 0)
+					{
+						if (pg_getnameinfo_all(&hba->addr, hba->addrlen,
+											   buffer, sizeof(buffer),
+											   NULL, 0,
+											   NI_NUMERICHOST) == 0)
+							clean_ipv6_addr(hba->addr.ss_family, buffer);
+						addrstr = pstrdup(buffer);
+					}
+					if (hba->masklen > 0)
+					{
+						if (pg_getnameinfo_all(&hba->mask, hba->masklen,
+											   buffer, sizeof(buffer),
+											   NULL, 0,
+											   NI_NUMERICHOST) == 0)
+							clean_ipv6_addr(hba->mask.ss_family, buffer);
+						maskstr = pstrdup(buffer);
+					}
+				}
+				break;
+			case ipCmpAll:
+				addrstr = "all";
+				break;
+			case ipCmpSameHost:
+				addrstr = "samehost";
+				break;
+			case ipCmpSameNet:
+				addrstr = "samenet";
+				break;
+		}
+		if (addrstr)
+			values[index++] = CStringGetTextDatum(addrstr);
+		else
+			nulls[index++] = true;
+		if (maskstr)
+			values[index++] = CStringGetTextDatum(maskstr);
+		else
+			nulls[index++] = true;
+
+		/* auth_method */
+		values[index++] = CStringGetTextDatum(hba_authname(hba->auth_method));
+
+		/* options */
+		options = get_hba_options(hba);
+		if (options)
+			values[index++] = PointerGetDatum(options);
+		else
+			nulls[index++] = true;
+	}
+	else
+	{
+		/* no parsing result, so set relevant fields to nulls */
+		memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool));
+	}
+
+	/* error */
+	if (err_msg)
+		values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg);
+	else
+		nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true;
+
+	tuple = heap_form_tuple(tupdesc, values, nulls);
+	tuplestore_puttuple(tuple_store, tuple);
+}
+
+/*
+ * fill_hba_view
+ *		Read the pg_hba.conf file and fill the tuplestore with view records.
+ */
+static void
+fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
+{
+	FILE	   *file;
+	List	   *hba_lines = NIL;
+	ListCell   *line;
+	MemoryContext linecxt;
+	MemoryContext hbacxt;
+	MemoryContext oldcxt;
+
+	/*
+	 * In the unlikely event that we can't open pg_hba.conf, we throw an
+	 * error, rather than trying to report it via some sort of view entry.
+	 * (Most other error conditions should result in a message in a view
+	 * entry.)
+	 */
+	file = AllocateFile(HbaFileName, "r");
+	if (file == NULL)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open configuration file \"%s\": %m",
+						HbaFileName)));
+
+	linecxt = tokenize_auth_file(HbaFileName, file, &hba_lines, DEBUG3);
+	FreeFile(file);
+
+	/* Now parse all the lines */
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
+								   "hba parser context",
+								   ALLOCSET_SMALL_SIZES);
+	oldcxt = MemoryContextSwitchTo(hbacxt);
+	foreach(line, hba_lines)
+	{
+		TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
+		HbaLine    *hbaline = NULL;
+
+		/* don't parse lines that already have errors */
+		if (tok_line->err_msg == NULL)
+			hbaline = parse_hba_line(tok_line, DEBUG3);
+
+		fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
+					  hbaline, tok_line->err_msg);
+	}
+
+	/* Free tokenizer memory */
+	MemoryContextDelete(linecxt);
+	/* Free parse_hba_line memory */
+	MemoryContextSwitchTo(oldcxt);
+	MemoryContextDelete(hbacxt);
+}
+
+/*
+ * pg_hba_file_rules
+ *
+ * SQL-accessible set-returning function to return all the entries in the
+ * pg_hba.conf file.
+ */
+Datum
+pg_hba_file_rules(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsi;
+
+	/*
+	 * Build tuplestore to hold the result rows.  We must use the Materialize
+	 * mode to be safe against HBA file changes while the cursor is open. It's
+	 * also more efficient than having to look up our current position in the
+	 * parsed list every time.
+	 */
+	SetSingleFuncCall(fcinfo, 0);
+
+	/* Fill the tuplestore */
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+	fill_hba_view(rsi->setResult, rsi->setDesc);
+
+	PG_RETURN_NULL();
+}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 93d5190508..49688036a7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -153,6 +153,7 @@ AttrMissing
 AttrNumber
 AttributeOpts
 AuthRequest
+AuthToken
 AutoPrewarmSharedState
 AutoVacOpts
 AutoVacuumShmemStruct
@@ -1063,7 +1064,6 @@ HashState
 HashTapeInfo
 HashValueFunc
 HbaLine
-HbaToken
 HeadlineJsonState
 HeadlineParsedText
 HeadlineWordEntry
@@ -2699,7 +2699,7 @@ ToastTupleContext
 ToastedAttribute
 TocEntry
 TokenAuxData
-TokenizedLine
+TokenizedAuthLine
 TrackItem
 TransInvalidationInfo
 TransState
-- 
2.35.1

Attachment: signature.asc
Description: PGP signature

Reply via email to