diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 29738b0..cd9f480 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7319,6 +7319,11 @@
+ pg_hba_rules
+ summary of client authentication configuration file contents
+
+
+ pg_groupgroups of database users
@@ -7857,6 +7862,139 @@
+
+ pg_hba_rules
+
+
+ pg_hba_rules
+
+
+
+ The view pg_hba_rules provides a summary of
+ the contents of the client authentication configuration file. A row
+ appears in this view for each entry appearing in the file, with annotations
+ indicating whether the rule could be applied successfully.
+
+
+
+ The pg_hba_rules view can be read only by
+ superusers.
+
+
+
+ pg_hba_rules> Columns
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+
+ line_number
+ integer
+
+ Line number within client authentication configuration file
+ the current value was set at
+
+
+
+ type
+ text
+ Type of connection
+
+
+ keyword_database
+ text[]
+
+ List of keyword database names,
+ name can be all, sameuser, samerole, replication and samegroup
+
+
+
+ database
+ text[]
+ List of database name
+
+
+ keyword_user
+ text[]
+
+ List of keyword user names,
+ name can be all and a group name prefixed with "+"
+
+
+
+ user_name
+ text[]
+ List of user names
+
+
+ keyword_address
+ text
+
+ List of keyword address names,
+ name can be all, samehost and samenet
+
+
+
+ address
+ inet
+ Client machine address
+
+
+ netmask
+ inet
+ Address mask
+
+
+ hostname
+ text
+ Client host name
+
+
+ method
+ text
+ Authentication method
+
+
+ options
+ text[]
+ Configuration options set for authentication method
+
+
+ error
+ text
+ If not null, an error message indicating why this entry could not be applied
+
+
+
+
+
+
+ If the configuration file contains any errors, error field
+ indicating the problem. Following is the sample output of the view.
+
+
+
+postgres=# select * from pg_hba_rules;
+ line_number | type | keyword_database | database | keyword_user | user_name | keyword_address | address | netmask | hostname | method | options | error
+-------------+-------+------------------+----------+--------------+-----------+-----------------+-----------+-----------------------------------------+----------+--------+---------+-------
+ 84 | local | {all} | | {all} | | | | | | trust | |
+ 86 | host | {all} | | {all} | | | 127.0.0.1 | 255.255.255.255 | | trust | |
+ 88 | host | {all} | | {all} | | | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | | trust | |
+(3 rows)
+
+
+
+ See for more information about the various
+ ways to change client authentication configuration.
+
+
+
pg_group
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 960f5b5..798956a 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -54,6 +54,13 @@
database user names and OS user names.
+
+ The system view
+ pg_hba_rules
+ can be helpful for pre-testing changes to the client authentication configuration file, or for
+ diagnosing problems if a SIGHUP> signal did not have the desired effects.
+
+
The pg_hba.conf File
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ada2142..3c79a57 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -427,6 +427,12 @@ CREATE VIEW pg_file_settings AS
REVOKE ALL on pg_file_settings FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+CREATE VIEW pg_hba_rules AS
+ SELECT * FROM pg_hba_rules() AS A;
+
+REVOKE ALL on pg_hba_rules FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_hba_rules() FROM PUBLIC;
+
CREATE VIEW pg_timezone_abbrevs AS
SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index f1e9a38..8747f0b 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,16 +25,23 @@
#include
#include
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
#include "common/ip.h"
+#include "funcapi.h"
#include "libpq/ifaddr.h"
#include "libpq/libpq.h"
+#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "regex/regex.h"
#include "replication/walsender.h"
#include "storage/fd.h"
#include "utils/acl.h"
+#include "utils/builtins.h"
#include "utils/guc.h"
+#include "utils/jsonb.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -75,6 +82,15 @@ typedef struct HbaToken
bool quoted;
} HbaToken;
+
+/* Context to use with lookup_hba_line_callback function. */
+typedef struct LookupHbaLineCxt
+{
+ MemoryContext memcxt;
+ TupleDesc tupdesc;
+ Tuplestorestate *tuple_store;
+} LookupHbaLineCxt;
+
/*
* pre-parsed content of HBA config file: list of HbaLine structs.
* parsed_hba_context is the memory context where it lives.
@@ -100,6 +116,10 @@ static List *tokenize_inc_file(List *tokens, const char *outer_filename,
const char *inc_filename);
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
int line_num);
+static Datum getauthmethod(UserAuth auth_method);
+static Datum gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, int lineno, HbaLine *hba, const char *err_msg);
+static bool token_is_a_database_keyword(HbaToken *tok);
/*
* isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -818,7 +838,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
* NULL.
*/
static HbaLine *
-parse_hba_line(List *line, int line_num, char *raw_line)
+parse_hba_line(List *line, int line_num, char *raw_line, char **err_msg, int level)
{
char *str;
struct addrinfo *gai_result;
@@ -841,12 +861,13 @@ parse_hba_line(List *line, int line_num, char *raw_line)
tokens = lfirst(field);
if (tokens->length > 1)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for connection type"),
errhint("Specify exactly one connection type per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("multiple values specified for connection type"));
return NULL;
}
token = linitial(tokens);
@@ -855,11 +876,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
#ifdef HAVE_UNIX_SOCKETS
parsedline->conntype = ctLocal;
#else
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("local connections are not supported by this build"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("local connections are not supported by this build"));
return NULL;
#endif
}
@@ -876,21 +898,23 @@ parse_hba_line(List *line, int line_num, char *raw_line)
parsedline->conntype = ctHostSSL;
else
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("hostssl requires SSL to be turned on"),
errhint("Set ssl = on in postgresql.conf."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("hostssl requires SSL to be turned on"));
return NULL;
}
#else
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("hostssl is not supported by this build"),
errhint("Compile with --with-openssl to use SSL connections."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("hostssl is not supported by this build"));
return NULL;
#endif
}
@@ -906,12 +930,13 @@ parse_hba_line(List *line, int line_num, char *raw_line)
} /* record type */
else
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid connection type \"%s\"",
token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("invalid connection type"));
return NULL;
}
@@ -919,11 +944,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
field = lnext(field);
if (!field)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before database specification"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("end-of-line before database specification"));
return NULL;
}
parsedline->databases = NIL;
@@ -938,11 +964,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
field = lnext(field);
if (!field)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before role specification"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("end-of-line before role specification"));
return NULL;
}
parsedline->roles = NIL;
@@ -959,22 +986,24 @@ parse_hba_line(List *line, int line_num, char *raw_line)
field = lnext(field);
if (!field)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before IP address specification"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("end-of-line before IP address specification"));
return NULL;
}
tokens = lfirst(field);
if (tokens->length > 1)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for host address"),
errhint("Specify one address range per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("multiple values specified for host address"));
return NULL;
}
token = linitial(tokens);
@@ -1024,7 +1053,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
parsedline->hostname = str;
else
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP address \"%s\": %s",
str, gai_strerror(ret)),
@@ -1032,6 +1061,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ *err_msg = pstrdup(_("invalid IP address"));
return NULL;
}
@@ -1042,24 +1072,26 @@ parse_hba_line(List *line, int line_num, char *raw_line)
{
if (parsedline->hostname)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("specifying both host name and CIDR mask is invalid"));
return NULL;
}
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid CIDR mask in address \"%s\"",
token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("invalid CIDR mask in address"));
return NULL;
}
pfree(str);
@@ -1071,22 +1103,24 @@ parse_hba_line(List *line, int line_num, char *raw_line)
field = lnext(field);
if (!field)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before netmask specification"),
errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("end-of-line before netmask specification"));
return NULL;
}
tokens = lfirst(field);
if (tokens->length > 1)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for netmask"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("multiple values specified for netmask"));
return NULL;
}
token = linitial(tokens);
@@ -1095,7 +1129,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
&hints, &gai_result);
if (ret || !gai_result)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\": %s",
token->string, gai_strerror(ret)),
@@ -1103,6 +1137,7 @@ parse_hba_line(List *line, int line_num, char *raw_line)
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ *err_msg = pstrdup(_("invalid IP mask"));
return NULL;
}
@@ -1112,11 +1147,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
if (parsedline->addr.ss_family != parsedline->mask.ss_family)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("IP address and mask do not match"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("IP address and mask do not match"));
return NULL;
}
}
@@ -1127,22 +1163,24 @@ parse_hba_line(List *line, int line_num, char *raw_line)
field = lnext(field);
if (!field)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before authentication method"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("end-of-line before authentication method"));
return NULL;
}
tokens = lfirst(field);
if (tokens->length > 1)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for authentication type"),
errhint("Specify exactly one authentication type per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("multiple values specified for authentication type"));
return NULL;
}
token = linitial(tokens);
@@ -1174,11 +1212,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
{
if (Db_user_namespace)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("MD5 authentication is not supported when \"db_user_namespace\" is enabled"));
return NULL;
}
parsedline->auth_method = uaMD5;
@@ -1211,23 +1250,25 @@ parse_hba_line(List *line, int line_num, char *raw_line)
parsedline->auth_method = uaRADIUS;
else
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\"",
token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("invalid authentication method"));
return NULL;
}
if (unsupauth)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\": not supported by this build",
token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("invalid authentication method, not supported by this build"));
return NULL;
}
@@ -1243,22 +1284,24 @@ parse_hba_line(List *line, int line_num, char *raw_line)
if (parsedline->conntype == ctLocal &&
parsedline->auth_method == uaGSS)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("gssapi authentication is not supported on local sockets"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("gssapi authentication is not supported on local sockets"));
return NULL;
}
if (parsedline->conntype != ctLocal &&
parsedline->auth_method == uaPeer)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("peer authentication is only supported on local sockets"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("peer authentication is only supported on local sockets"));
return NULL;
}
@@ -1271,11 +1314,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
if (parsedline->conntype != ctHostSSL &&
parsedline->auth_method == uaCert)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cert authentication is only supported on hostssl connections"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("cert authentication is only supported on hostssl connections"));
return NULL;
}
@@ -1320,11 +1364,12 @@ parse_hba_line(List *line, int line_num, char *raw_line)
/*
* Got something that's not a name=value pair.
*/
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("authentication option not in name=value format: %s", token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("authentication option not in name=value format"));
return NULL;
}
@@ -1357,21 +1402,23 @@ parse_hba_line(List *line, int line_num, char *raw_line)
parsedline->ldapbindpasswd ||
parsedline->ldapsearchattribute)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"));
return NULL;
}
}
else if (!parsedline->ldapbasedn)
{
- ereport(LOG,
+ ereport(level,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
+ *err_msg = pstrdup(_("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"));
return NULL;
}
}
@@ -1784,7 +1831,7 @@ check_hba(hbaPort *port)
* with the old data.
*/
bool
-load_hba(void)
+load_hba(hba_line_callback callback, void *callback_context)
{
FILE *file;
List *hba_lines = NIL;
@@ -1798,6 +1845,8 @@ load_hba(void)
MemoryContext linecxt;
MemoryContext oldcxt;
MemoryContext hbacxt;
+ char *err_msg = NULL;
+ int log_level = LOG;
file = AllocateFile(HbaFileName, "r");
if (file == NULL)
@@ -1812,18 +1861,24 @@ load_hba(void)
linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines);
FreeFile(file);
+ if (callback)
+ log_level = DEBUG3;
+
/* Now parse all the lines */
- Assert(PostmasterContext);
- hbacxt = AllocSetContextCreate(PostmasterContext,
+ hbacxt = AllocSetContextCreate(CurrentMemoryContext,
"hba parser context",
ALLOCSET_SMALL_SIZES);
oldcxt = MemoryContextSwitchTo(hbacxt);
forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines)
{
HbaLine *newline;
+ int lineno = lfirst_int(line_num);
- if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line))) == NULL)
+ if ((newline = parse_hba_line(lfirst(line), lineno, lfirst(raw_line), &err_msg, log_level)) == NULL)
{
+ if (callback)
+ callback(callback_context, lineno, newline, err_msg);
+
/*
* Parse error in the file, so indicate there's a problem. NB: a
* problem in a line will free the memory for all previous lines
@@ -1841,10 +1896,24 @@ load_hba(void)
continue;
}
+ if (callback)
+ callback(callback_context, lineno, newline, NULL);
new_parsed_lines = lappend(new_parsed_lines, newline);
}
/*
+ * If callback function is available, then don't update the saved
+ * authentication rules.
+ */
+ if (callback)
+ {
+ MemoryContextDelete(linecxt);
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextDelete(hbacxt);
+ return true;
+ }
+
+ /*
* A valid HBA file must have at least one entry; else there's no way to
* connect to the postmaster. But only complain about this if we didn't
* already have parsing errors.
@@ -1870,14 +1939,25 @@ load_hba(void)
}
/* Loaded new file successfully, replace the one we use */
- if (parsed_hba_context != NULL)
- MemoryContextDelete(parsed_hba_context);
+ discard_hba();
parsed_hba_context = hbacxt;
parsed_hba_lines = new_parsed_lines;
return true;
}
+void
+discard_hba(void)
+{
+ if (parsed_hba_context != NULL)
+ {
+ MemoryContextDelete(parsed_hba_context);
+ parsed_hba_context = NULL;
+ parsed_hba_lines = NIL;
+ }
+}
+
+
/*
* Parse one tokenised line from the ident config file and store the result in
* an IdentLine structure, or NULL if parsing fails.
@@ -2189,8 +2269,7 @@ load_ident(void)
FreeFile(file);
/* Now parse all the lines */
- Assert(PostmasterContext);
- ident_context = AllocSetContextCreate(PostmasterContext,
+ ident_context = AllocSetContextCreate(CurrentMemoryContext,
"ident parser context",
ALLOCSET_SMALL_SIZES);
oldcxt = MemoryContextSwitchTo(ident_context);
@@ -2241,6 +2320,19 @@ load_ident(void)
}
/* Loaded new file successfully, replace the one we use */
+ discard_ident();
+ parsed_ident_context = ident_context;
+ parsed_ident_lines = new_parsed_lines;
+
+ return true;
+}
+
+void
+discard_ident(void)
+{
+ ListCell *parsed_line_cell;
+ IdentLine *newline;
+
if (parsed_ident_lines != NIL)
{
foreach(parsed_line_cell, parsed_ident_lines)
@@ -2249,14 +2341,15 @@ load_ident(void)
if (newline->ident_user[0] == '/')
pg_regfree(&newline->re);
}
+
+ parsed_ident_lines = NIL;
}
+
if (parsed_ident_context != NULL)
+ {
MemoryContextDelete(parsed_ident_context);
-
- parsed_ident_context = ident_context;
- parsed_ident_lines = new_parsed_lines;
-
- return true;
+ parsed_ident_context = NULL;
+ }
}
@@ -2274,3 +2367,582 @@ hba_getauthmethod(hbaPort *port)
{
check_hba(port);
}
+
+/*
+ * Returns the Text Datum representation of authentication method
+ */
+static Datum
+getauthmethod(UserAuth auth_method)
+{
+ Datum result;
+
+ switch (auth_method)
+ {
+ case uaReject:
+ result = CStringGetTextDatum("reject");
+ break;
+ case uaTrust:
+ result = CStringGetTextDatum("trust");
+ break;
+ case uaIdent:
+ result = CStringGetTextDatum("ident");
+ break;
+ case uaPassword:
+ result = CStringGetTextDatum("password");
+ break;
+ case uaMD5:
+ result = CStringGetTextDatum("md5");
+ break;
+ case uaGSS:
+ result = CStringGetTextDatum("gss");
+ break;
+ case uaSSPI:
+ result = CStringGetTextDatum("sspi");
+ break;
+ case uaPAM:
+ result = CStringGetTextDatum("pam");
+ break;
+ case uaLDAP:
+ result = CStringGetTextDatum("ldap");
+ break;
+ case uaCert:
+ result = CStringGetTextDatum("cert");
+ break;
+ case uaRADIUS:
+ result = CStringGetTextDatum("radius");
+ break;
+ case uaPeer:
+ result = CStringGetTextDatum("peer");
+ break;
+ default:
+ elog(ERROR, "unexpected authentication method in parsed HBA entry");
+ break;
+ }
+
+ return result;
+}
+
+/* LDAP supports 10 currently, keep this well above the most any method needs */
+#define MAX_OPTIONS 12
+
+static Datum
+gethba_options(HbaLine *hba)
+{
+ StringInfoData str;
+ int noptions;
+ Datum options[MAX_OPTIONS];
+ char buffer[64];
+
+ 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)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "krb_realm=");
+ appendStringInfoString(&str, hba->krb_realm);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+ }
+
+ if (hba->usermap)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "map=");
+ appendStringInfoString(&str, hba->usermap);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->clientcert)
+ options[noptions++] = CStringGetTextDatum("clientcert=true");
+
+ if (hba->pamservice)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "pamservice=");
+ appendStringInfoString(&str, hba->pamservice);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->auth_method == uaLDAP)
+ {
+ if (hba->ldapserver)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapserver=");
+ appendStringInfoString(&str, hba->ldapserver);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapport)
+ {
+ initStringInfo(&str);
+ snprintf(buffer, sizeof(buffer), "%d", hba->ldapport);
+ appendStringInfoString(&str, "ldapport=");
+ appendStringInfoString(&str, buffer);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldaptls)
+ options[noptions++] = CStringGetTextDatum("ldaptls=true");
+
+ if (hba->ldapprefix)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapprefix=");
+ appendStringInfoString(&str, hba->ldapprefix);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapsuffix)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapsuffix=");
+ appendStringInfoString(&str, hba->ldapsuffix);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapbasedn)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapbasedn=");
+ appendStringInfoString(&str, hba->ldapbasedn);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapbinddn)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapbinddn=");
+ appendStringInfoString(&str, hba->ldapbinddn);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapbindpasswd)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapbindpasswd=");
+ appendStringInfoString(&str, hba->ldapbindpasswd);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapsearchattribute)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "ldapsearchattribute=");
+ appendStringInfoString(&str, hba->ldapsearchattribute);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->ldapscope)
+ {
+ initStringInfo(&str);
+ snprintf(buffer, sizeof(buffer), "%d", hba->ldapscope);
+ appendStringInfoString(&str, "ldapscope=");
+ appendStringInfoString(&str, buffer);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+ }
+
+ if (hba->auth_method == uaRADIUS)
+ {
+ if (hba->radiusserver)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "radiusserver=");
+ appendStringInfoString(&str, hba->radiusserver);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->radiussecret)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "radiussecret=");
+ appendStringInfoString(&str, hba->radiussecret);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->radiusidentifier)
+ {
+ initStringInfo(&str);
+ appendStringInfoString(&str, "radiusidentifier=");
+ appendStringInfoString(&str, hba->radiusidentifier);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+
+ if (hba->radiusport)
+ {
+ initStringInfo(&str);
+ snprintf(buffer, sizeof(buffer), "%d", hba->radiusport);
+ appendStringInfoString(&str, "radiusport=");
+ appendStringInfoString(&str, buffer);
+ options[noptions++] = CStringGetTextDatum(str.data);
+ }
+ }
+
+ Assert(noptions <= MAX_OPTIONS);
+ if (noptions)
+ return PointerGetDatum(
+ construct_array(options, noptions, TEXTOID, -1, false, 'i'));
+ return PointerGetDatum(NULL);
+}
+
+static bool
+token_is_a_database_keyword(HbaToken *tok)
+{
+ if (token_is_keyword(tok, "replication")
+ || token_is_keyword(tok, "all")
+ || token_is_keyword(tok, "sameuser")
+ || token_is_keyword(tok, "samegroup")
+ || token_is_keyword(tok, "samerole"))
+ return true;
+ return false;
+}
+
+#define NUM_PG_HBA_LOOKUP_ATTS 13
+
+static void
+lookup_hba_line_callback(void *context, int lineno, HbaLine *hba, const char *err_msg)
+{
+ Datum values[NUM_PG_HBA_LOOKUP_ATTS];
+ bool nulls[NUM_PG_HBA_LOOKUP_ATTS];
+ ListCell *dbcell;
+ char buffer[NI_MAXHOST];
+ HeapTuple tuple;
+ int index;
+ Datum options;
+ MemoryContext old_cxt;
+ LookupHbaLineCxt *mycxt;
+
+ mycxt = (LookupHbaLineCxt *) context;
+
+ index = 0;
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+ /* line_number */
+ values[index] = Int32GetDatum(lineno);
+
+ if (err_msg)
+ {
+ /* type */
+ index++;
+ nulls[index] = true;
+
+ /* keyword_database */
+ index++;
+ nulls[index] = true;
+
+ /* database */
+ index++;
+ nulls[index] = true;
+
+ /* keyword_user */
+ index++;
+ nulls[index] = true;
+
+ /* user */
+ index++;
+ nulls[index] = true;
+
+ /* keyword_address */
+ index++;
+ nulls[index] = true;
+
+ /* address */
+ index++;
+ nulls[index] = true;
+
+ /* netmask */
+ index++;
+ nulls[index] = true;
+
+ /* hostname */
+ index++;
+ nulls[index] = true;
+
+ /* method */
+ index++;
+ nulls[index] = true;
+
+ /* options */
+ index++;
+ nulls[index] = true;
+
+ /* error */
+ index++;
+ values[index] = CStringGetTextDatum(err_msg);
+ }
+ else
+ {
+ /* type */
+ index++;
+ switch (hba->conntype)
+ {
+ case ctLocal:
+ values[index] = CStringGetTextDatum("local");
+ break;
+ case ctHost:
+ values[index] = CStringGetTextDatum("host");
+ break;
+ case ctHostSSL:
+ values[index] = CStringGetTextDatum("hostssl");
+ break;
+ case ctHostNoSSL:
+ values[index] = CStringGetTextDatum("hostnossl");
+ break;
+ default:
+ elog(ERROR, "unexpected connection type in parsed HBA entry");
+ break;
+ }
+
+ /* keyword_database and database */
+ index++;
+ if (list_length(hba->databases) != 0)
+ {
+ List *names = NULL;
+ List *keyword_names = NULL;
+ HbaToken *tok;
+
+ foreach(dbcell, hba->databases)
+ {
+ tok = lfirst(dbcell);
+ if (token_is_a_database_keyword(tok))
+ keyword_names = lappend(keyword_names, tok->string);
+ else
+ names = lappend(names, tok->string);
+ }
+
+ /* keyword_database */
+ if (keyword_names != NULL)
+ values[index] = PointerGetDatum(strlist_to_textarray(keyword_names));
+ else
+ nulls[index] = true;
+
+ /* database */
+ index++;
+ if (names != NULL)
+ values[index] = PointerGetDatum(strlist_to_textarray(names));
+ else
+ nulls[index] = true;
+ }
+ else
+ {
+ nulls[index] = true;
+ index++;
+ nulls[index] = true;
+ }
+
+ /* keyword_user and user */
+ index++;
+ if (list_length(hba->roles) != 0)
+ {
+ List *roles = NULL;
+ List *keyword_roles = NULL;
+ HbaToken *tok;
+
+ foreach(dbcell, hba->roles)
+ {
+ tok = lfirst(dbcell);
+ if (token_is_keyword(tok, "all"))
+ keyword_roles = lappend(keyword_roles, tok->string);
+ else
+ roles = lappend(roles, tok->string);
+ }
+
+ /* keyword_user */
+ if (keyword_roles != NULL)
+ values[index] = PointerGetDatum(strlist_to_textarray(keyword_roles));
+ else
+ nulls[index] = true;
+
+ /* user */
+ index++;
+ if (roles != NULL)
+ values[index] = PointerGetDatum(strlist_to_textarray(roles));
+ else
+ nulls[index] = true;
+ }
+ else
+ {
+ nulls[index] = true;
+ index++;
+ nulls[index] = true;
+ }
+
+ /* keyword_address */
+ index++;
+ switch (hba->ip_cmp_method)
+ {
+ case ipCmpAll:
+ values[index] = CStringGetTextDatum("all");
+ break;
+ case ipCmpSameHost:
+ values[index] = CStringGetTextDatum("samehost");
+ break;
+ case ipCmpSameNet:
+ values[index] = CStringGetTextDatum("samenet");
+ break;
+ default:
+ nulls[index] = true;
+ break;
+ }
+
+ /* address */
+ index++;
+ if (pg_getnameinfo_all(&hba->addr, sizeof(struct sockaddr_storage),
+ buffer, sizeof(buffer),
+ NULL, 0,
+ NI_NUMERICHOST) == 0)
+ {
+ clean_ipv6_addr(hba->addr.ss_family, buffer);
+ values[index] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ }
+ else
+ nulls[index] = true;
+
+ /* netmask */
+ index++;
+ if (pg_getnameinfo_all(&hba->mask, sizeof(struct sockaddr_storage),
+ buffer, sizeof(buffer),
+ NULL, 0,
+ NI_NUMERICHOST) == 0)
+ {
+ clean_ipv6_addr(hba->mask.ss_family, buffer);
+ values[index] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ }
+ else
+ nulls[index] = true;
+
+ /* hostname */
+ index++;
+ if (hba->hostname)
+ values[index] = CStringGetTextDatum(hba->hostname);
+ else
+ nulls[index] = true;
+
+ /* method */
+ index++;
+ values[index] = getauthmethod(hba->auth_method);
+
+ /* options */
+ index++;
+ options = gethba_options(hba);
+ if (options)
+ values[index] = PointerGetDatum(options);
+ else
+ nulls[index] = true;
+
+ /* error */
+ index++;
+ nulls[index] = true;
+ }
+
+ tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+ tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+ MemoryContextSwitchTo(old_cxt);
+ return;
+}
+
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+hba_rules(PG_FUNCTION_ARGS)
+{
+ Tuplestorestate *tuple_store;
+ TupleDesc tupdesc;
+ MemoryContext old_cxt;
+ LookupHbaLineCxt *mycxt;
+ ReturnSetInfo *rsi;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+ /*
+ * We must use the Materialize mode to be safe against HBA file reloads
+ * while the cursor is open. It's also more efficient than having to look
+ * up our current position in the parsed list every time.
+ */
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ (rsi->allowedModes & SFRM_Materialize) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ rsi->returnMode = SFRM_Materialize;
+
+ /*
+ * Create the tupledesc and tuplestore in the per_query context as
+ * required for SFRM_Materialize.
+ */
+ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+ tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "type",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "keyword_database",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 5, "keyword_user",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 6, "user_name",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 7, "keyword_address",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 8, "address",
+ INETOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 9, "netmask",
+ INETOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 10, "hostname",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 11, "method",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 12, "options",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 13, "error",
+ TEXTOID, -1, 0);
+ BlessTupleDesc(tupdesc);
+
+ tuple_store =
+ tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ false, work_mem);
+
+ MemoryContextSwitchTo(old_cxt);
+
+ mycxt = (LookupHbaLineCxt *) palloc(sizeof(LookupHbaLineCxt));
+ mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "pg_hba_lookup tuple cxt",
+ ALLOCSET_DEFAULT_SIZES);
+ mycxt->tupdesc = tupdesc;
+ mycxt->tuple_store = tuple_store;
+
+ if (!load_hba(lookup_hba_line_callback, mycxt))
+ ereport(ERROR,
+ (errmsg("failed to load pg_hba.conf file"),
+ errhint("more details may be available in the server log.")));
+
+ MemoryContextDelete(mycxt->memcxt);
+ pfree(mycxt);
+
+ rsi->setDesc = tupdesc;
+ rsi->setResult = tuple_store;
+
+ PG_RETURN_NULL();
+}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74..3630f07 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1251,7 +1251,7 @@ PostmasterMain(int argc, char *argv[])
/*
* Load configuration files for client authentication.
*/
- if (!load_hba())
+ if (!load_hba(NULL, NULL))
{
/*
* It makes no sense to continue if we fail to load the HBA file,
@@ -2503,7 +2503,7 @@ SIGHUP_handler(SIGNAL_ARGS)
signal_child(PgStatPID, SIGHUP);
/* Reload authentication config files too */
- if (!load_hba())
+ if (!load_hba(NULL, NULL))
ereport(WARNING,
(errmsg("pg_hba.conf not reloaded")));
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 824d5ab..19446bd 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -192,17 +192,6 @@ PerformAuthentication(Port *port)
* FIXME: [fork/exec] Ugh. Is there a way around this overhead?
*/
#ifdef EXEC_BACKEND
-
- /*
- * load_hba() and load_ident() want to work within the PostmasterContext,
- * so create that if it doesn't exist (which it won't). We'll delete it
- * again later, in PostgresMain.
- */
- if (PostmasterContext == NULL)
- PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- "Postmaster",
- ALLOCSET_DEFAULT_SIZES);
-
if (!load_hba())
{
/*
@@ -737,6 +726,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
am_superuser = superuser();
}
+ /*
+ * We don't need the HBA and ident data going forward, but we can't rely
+ * on release of PostmasterContext to clean that up, so discard them
+ * explicitly here.
+ */
+ discard_hba();
+ discard_ident();
+
/*
* If we're trying to shut down, only superusers can connect, and new
* replication connections are not allowed.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 17ec71d..85e5672 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3066,6 +3066,8 @@ DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f
DESCR("SHOW ALL as a function");
DATA(insert OID = 3329 ( pg_show_all_file_settings PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,23,23,25,25,16,25}" "{o,o,o,o,o,o,o}" "{sourcefile,sourceline,seqno,name,setting,applied,error}" _null_ _null_ show_all_file_settings _null_ _null_ _null_ ));
DESCR("show config file settings");
+DATA(insert OID = 3343 ( pg_hba_rules PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{23,25,1009,1009,1009,1009,25,869,869,25,25,1009,25}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{line_number,type,keyword_database,database,keyword_user,user_name,keyword_address,address,netmask,hostname,method,options,error}" _null_ _null_ hba_rules _null_ _null_ _null_ ));
+DESCR("show pg_hba config rules");
DATA(insert OID = 1371 ( pg_lock_status PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,26,26,23,21,25,28,26,26,21,25,23,25,16,16}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{locktype,database,relation,page,tuple,virtualxid,transactionid,classid,objid,objsubid,virtualtransaction,pid,mode,granted,fastpath}" _null_ _null_ pg_lock_status _null_ _null_ _null_ ));
DESCR("view system lock information");
DATA(insert OID = 2561 ( pg_blocking_pids PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 1007 "23" _null_ _null_ _null_ _null_ _null_ pg_blocking_pids _null_ _null_ _null_ ));
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index dc7d257..970f8c5 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -100,8 +100,20 @@ typedef struct IdentLine
/* kluge to avoid including libpq/libpq-be.h here */
typedef struct Port hbaPort;
-extern bool load_hba(void);
+/*
+ * Optional callback function type for load_hba() function.
+ * Currently, valid callback function is passed to load_hba()
+ * by pg_hba_rules function view to frame the hba tuple.
+ */
+typedef void (*hba_line_callback) (void *context, int line_num,
+ HbaLine *hba_line, const char *err_msg);
+
+extern bool load_hba(hba_line_callback callback, void *callback_context);
extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
extern void hba_getauthmethod(hbaPort *port);
extern int check_usermap(const char *usermap_name,
const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 90f5132..93cc6f1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1189,6 +1189,9 @@ extern Datum pg_control_recovery(PG_FUNCTION_ARGS);
extern Datum row_security_active(PG_FUNCTION_ARGS);
extern Datum row_security_active_name(PG_FUNCTION_ARGS);
+/* hba.c */
+extern Datum hba_rules(PG_FUNCTION_ARGS);
+
/* lockfuncs.c */
extern Datum pg_lock_status(PG_FUNCTION_ARGS);
extern Datum pg_blocking_pids(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 00700f2..f26f256 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1338,6 +1338,20 @@ pg_group| SELECT pg_authid.rolname AS groname,
WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
FROM pg_authid
WHERE (NOT pg_authid.rolcanlogin);
+pg_hba_rules| SELECT a.line_number,
+ a.type,
+ a.keyword_database,
+ a.database,
+ a.keyword_user,
+ a.user_name,
+ a.keyword_address,
+ a.address,
+ a.netmask,
+ a.hostname,
+ a.method,
+ a.options,
+ a.error
+ FROM pg_hba_rules() a(line_number, type, keyword_database, database, keyword_user, user_name, keyword_address, address, netmask, hostname, method, options, error);
pg_indexes| SELECT n.nspname AS schemaname,
c.relname AS tablename,
i.relname AS indexname,
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source
index dd2d1b2..3cdd2d7 100644
--- a/src/test/regress/input/misc.source
+++ b/src/test/regress/input/misc.source
@@ -2,6 +2,9 @@
-- MISC
--
+-- catalog view to pg_hba.conf
+SELECT * FROM pg_hba_rules;
+
--
-- BTREE
--
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source
index 574ef0d..e286b09 100644
--- a/src/test/regress/output/misc.source
+++ b/src/test/regress/output/misc.source
@@ -1,6 +1,15 @@
--
-- MISC
--
+-- catalog view to pg_hba.conf
+SELECT * FROM pg_hba_rules;
+ line_number | type | keyword_database | database | keyword_user | user_name | keyword_address | address | netmask | hostname | method | options | error
+-------------+-------+------------------+----------+--------------+-----------+-----------------+-----------+-----------------------------------------+----------+--------+---------+-------
+ 84 | local | {all} | | {all} | | | | | | trust | |
+ 86 | host | {all} | | {all} | | | 127.0.0.1 | 255.255.255.255 | | trust | |
+ 88 | host | {all} | | {all} | | | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | | trust | |
+(3 rows)
+
--
-- BTREE
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 6c6d519..cbfbe16 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1118,6 +1118,7 @@ LogicalOutputPluginWriterWrite
LogicalRewriteMappingData
LogicalTape
LogicalTapeSet
+LookupHbaLineCxt
MAGIC
MBuf
MEMORY_BASIC_INFORMATION