diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8ef9fce..215c7c0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16576,6 +16576,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -16633,6 +16646,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | database | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 536c805..9ffc4fa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -948,3 +948,12 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+	OUT line_number int, OUT mode text, OUT type text, OUT database text[],
+	OUT user_name text[], OUT address inet, OUT netmask inet,
+        OUT hostname text, OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 58a4565..fb1dbe7 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS   11
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -74,6 +83,18 @@ typedef struct HbaToken
 	bool		quoted;
 } HbaToken;
 
+/* Optional callback function type for check_hba(). */
+typedef void (*check_hba_line_callback)(void *context, HbaLine *hba_line,
+			  const char *reason);
+
+/* Context to use with lookup_hba_line_callback(). */
+typedef struct {
+	MemoryContext		memcxt;
+	TupleDesc			tupdesc;
+	Tuplestorestate	   *tuple_store;
+} LookupHbalineContext;
+
+
 /*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
@@ -99,6 +120,13 @@ 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 void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -1640,7 +1668,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, check_hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1686,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, "connection type mismatch");
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, "connection type mismatch");
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, "connection type mismatch");
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, "connection type mismatch");
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1731,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, "hostname mismatch");
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, "IP address mismatch");
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1755,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, "samehost/samenet mismatch");
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1770,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, "database name mismatch");
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, "user name mismatch");
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -2256,5 +2324,551 @@ discard_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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;
+}
+
+static void
+hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+static void
+hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static void
+hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static void
+hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState	   *parseState = NULL;
+	JsonbValue		   *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			hba_add_jsonb_string_key(&parseState, "include_realm");
+			hba_add_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			hba_add_jsonb_string_key(&parseState, "krb_realm");
+			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		hba_add_jsonb_string_key(&parseState, "map");
+		hba_add_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		hba_add_jsonb_string_key(&parseState, "clientcert");
+		hba_add_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		hba_add_jsonb_string_key(&parseState, "pamservice");
+		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapserver");
+			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapport");
+			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldaptls");
+			hba_add_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapscope");
+			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusserver");
+			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiussecret");
+			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusport");
+			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum			values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool			nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell	   *dbcell;
+	char			buffer[NI_MAXHOST];
+	HeapTuple		tuple;
+	int				index;
+	MemoryContext	old_cxt;
+	LookupHbalineContext   *mycxt;
+
+	mycxt = (LookupHbalineContext *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum("matched");
+	else
+		values[index] = CStringGetTextDatum("skipped");
+
+	/* 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;
+	}
+
+	/* database */
+	index++;
+	if (list_length(hba->databases) != 0)
+	{
+		List	   *names = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->databases)
+		{
+			tok = lfirst(dbcell);
+			names = lappend(names, tok->string);
+		}
+
+		values[index] = PointerGetDatum(strlist_to_textarray(names));
+	}
+	else
+		nulls[index] = true;
+
+	/* user */
+	index++;
+	if (list_length(hba->roles) != 0)
+	{
+		List	   *roles = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->roles)
+		{
+			tok = lfirst(dbcell);
+			roles = lappend(roles, tok->string);
+		}
+
+		values[index] = PointerGetDatum(strlist_to_textarray(roles));
+	}
+	else
+		nulls[index] = true;
+
+	/* 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++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		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
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort				   *port;
+	Tuplestorestate		   *tuple_store;
+	TupleDesc				tupdesc;
+	MemoryContext			old_cxt;
+	LookupHbalineContext   *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")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+						(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+								port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("Failed to load pg_hba.conf file"),
+				 errhint("More details may be available in the server log.")));
+
+	/*
+	 * 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, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (LookupHbalineContext *) palloc(sizeof(LookupHbalineContext));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..ebd96da 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3079,6 +3079,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e610bf3..2d60879 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1123,6 +1123,9 @@ extern Datum set_config_by_name(PG_FUNCTION_ARGS);
 extern Datum show_all_settings(PG_FUNCTION_ARGS);
 extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
