diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c0b94bc..4583400 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16751,6 +16751,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
text
set parameter and return new value
+
+
+
+ pg_hba_lookup
+
+ pg_hba_lookup(database> text>,
+ user_name> text>
+ [, address> text>]
+ [, ssl_inuse> text>)
+
+ record
+ scan pg_hba.conf> and return examined entries up to a matching one
+
@@ -16808,6 +16821,32 @@ SELECT set_config('log_statement_stats', 'off', false);
+
+ pg_hba_lookup 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
+ kommih> trying to connect to a database postgres>
+ from IPv6-address ::1>, one can issue a following query:
+
+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)
+
+
+ This function actually loads the contents of 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
+ pg_hba.conf> entries up.
+
+
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index abf9a70..615a4d5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -971,3 +971,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 28f9fb5..d0099dc 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
#include
#include
+#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
{
@@ -75,6 +84,24 @@ typedef struct HbaToken
} HbaToken;
/*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+ const char *reason);
+
+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+ MemoryContext memcxt;
+ TupleDesc tupdesc;
+ Tuplestorestate *tuple_store;
+} HbalineContext;
+
+
+/*
* pre-parsed content of HBA config file: list of HbaLine structs.
* parsed_hba_context is the memory context where it lives.
*/
@@ -99,6 +126,9 @@ 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 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 +1670,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, hba_line_callback callback,
+ void *callback_context)
{
Oid roleid;
ListCell *line;
@@ -1657,25 +1688,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 +1733,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 +1757,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 +1772,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;
@@ -1770,8 +1840,7 @@ load_hba(void)
FreeFile(file);
/* Now parse all the lines */
- Assert(PostmasterContext);
- hbacxt = AllocSetContextCreate(PostmasterContext,
+ hbacxt = AllocSetContextCreate(CurrentMemoryContext,
"hba parser context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1898,26 @@ 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.
@@ -2148,8 +2229,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_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2282,20 @@ 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)
@@ -2210,18 +2304,17 @@ 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;
+ }
}
-
/*
* Determine what authentication method should be used when accessing database
* "database" from frontend "raddr", user "user". Return the method and
@@ -2233,5 +2326,505 @@ load_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 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)
+ {
+ push_jsonb_string_key(&parseState, "include_realm");
+ push_jsonb_bool_value(&parseState, true);
+ }
+
+ if (hba->krb_realm)
+ {
+ push_jsonb_string_key(&parseState, "krb_realm");
+ push_jsonb_string_value(&parseState, hba->krb_realm);
+ }
+ }
+
+ if (hba->usermap)
+ {
+ push_jsonb_string_key(&parseState, "map");
+ push_jsonb_string_value(&parseState, hba->usermap);
+ }
+
+ if (hba->clientcert)
+ {
+ push_jsonb_string_key(&parseState, "clientcert");
+ push_jsonb_bool_value(&parseState, true);
+ }
+
+ if (hba->pamservice)
+ {
+ push_jsonb_string_key(&parseState, "pamservice");
+ push_jsonb_string_value(&parseState, hba->pamservice);
+ }
+
+ if (hba->auth_method == uaLDAP)
+ {
+ if (hba->ldapserver)
+ {
+ push_jsonb_string_key(&parseState, "ldapserver");
+ push_jsonb_string_value(&parseState, hba->ldapserver);
+ }
+
+ if (hba->ldapport)
+ {
+ push_jsonb_string_key(&parseState, "ldapport");
+ push_jsonb_int32_value(&parseState, hba->ldapport);
+ }
+
+ if (hba->ldaptls)
+ {
+ push_jsonb_string_key(&parseState, "ldaptls");
+ push_jsonb_bool_value(&parseState, true);
+ }
+
+ if (hba->ldapprefix)
+ {
+ push_jsonb_string_key(&parseState, "ldapprefix");
+ push_jsonb_string_value(&parseState, hba->ldapprefix);
+ }
+
+ if (hba->ldapsuffix)
+ {
+ push_jsonb_string_key(&parseState, "ldapsuffix");
+ push_jsonb_string_value(&parseState, hba->ldapsuffix);
+ }
+
+ if (hba->ldapbasedn)
+ {
+ push_jsonb_string_key(&parseState, "ldapbasedn");
+ push_jsonb_string_value(&parseState, hba->ldapbasedn);
+ }
+
+ if (hba->ldapbinddn)
+ {
+ push_jsonb_string_key(&parseState, "ldapbinddn");
+ push_jsonb_string_value(&parseState, hba->ldapbinddn);
+ }
+
+ if (hba->ldapbindpasswd)
+ {
+ push_jsonb_string_key(&parseState, "ldapbindpasswd");
+ push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+ }
+
+ if (hba->ldapsearchattribute)
+ {
+ push_jsonb_string_key(&parseState, "ldapsearchattribute");
+ push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+ }
+
+ if (hba->ldapscope)
+ {
+ push_jsonb_string_key(&parseState, "ldapscope");
+ push_jsonb_int32_value(&parseState, hba->ldapscope);
+ }
+ }
+
+ if (hba->auth_method == uaRADIUS)
+ {
+ if (hba->radiusserver)
+ {
+ push_jsonb_string_key(&parseState, "radiusserver");
+ push_jsonb_string_value(&parseState, hba->radiusserver);
+ }
+
+ if (hba->radiussecret)
+ {
+ push_jsonb_string_key(&parseState, "radiussecret");
+ push_jsonb_string_value(&parseState, hba->radiussecret);
+ }
+
+ if (hba->radiusidentifier)
+ {
+ push_jsonb_string_key(&parseState, "radiusidentifier");
+ push_jsonb_string_value(&parseState, hba->radiusidentifier);
+ }
+
+ if (hba->radiusport)
+ {
+ push_jsonb_string_key(&parseState, "radiusport");
+ push_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;
+ HbalineContext *mycxt;
+
+ mycxt = (HbalineContext *) 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;
+ HbalineContext *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 = (HbalineContext *) palloc(sizeof(HbalineContext));
+ 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/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
object->val.object.nPairs = res + 1 - object->val.object.pairs;
}
}
+
+void
+push_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);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+ JsonbValue jb;
+
+ jb.type = jbvBool;
+ jb.val.boolean = bool_val;
+
+ pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_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);
+}
+
+void
+push_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);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 6b760d4..d92cd9a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -191,18 +191,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_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
if (!load_hba())
{
/*
@@ -736,6 +724,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
}
/*
+ * 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 aec6c4c..3253382 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3010,6 +3010,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 = 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 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
extern bool load_hba(void);
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 7ec93c9..24625e5 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1151,6 +1151,9 @@ extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
/* pg_config.c */
extern Datum pg_config(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);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
/* jsonb.c support functions */
extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
int estimated_len);