*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE"); --- 16576,16594 ---- text set parameter and return new value + + + + pg_hba_lookup + + pg_hba_lookup(database text, + user text + [, address text] + [, hostname text) + + record + Returns all matching row entries of pg_hba.conf + *************** *** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false); --- 16646,16667 ---- + + pg_hba_lookup returns a set of records + containing the line number, type, database, user, address, + hostname, method and options that are satisfied with the given + input values. Typical usages include: + + SELECT pg_hba_lookup('test_database', 'test_user'); + + pg_hba_lookup + -------------------------------------------------------------- + (84,local,"[""all""]","[""all""]",,,trust,{}) + (86,local,"[""test_database""]","[""test_user""]",,,trust,{}) + (1 row) + + + *** a/src/backend/libpq/hba.c --- b/src/backend/libpq/hba.c *************** *** 25,39 **** --- 25,45 ---- #include #include + #include "access/htup_details.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" *************** *** 74,79 **** typedef struct HbaToken --- 80,95 ---- bool quoted; } HbaToken; + typedef enum hba_lookup_args_mode + { + TWO_ARGS_MODE=0, + THREE_ARGS_MODE, + FOUR_ARGS_MODE + }hba_lookup_args_mode; + + /* Flag to indicate the failure of reloading pg_hba.conf file */ + bool load_hba_failure = false; + /* * pre-parsed content of HBA config file: list of HbaLine structs. * parsed_hba_context is the memory context where it lives. *************** *** 100,105 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename, --- 116,128 ---- static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num); + static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, + bool hba_match_result); + static bool pg_hba_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage *addr, + char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode); + static void pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage *addr, + char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi); + /* * isblank() exists in the ISO C99 spec, but it's not very portable yet, * so provide our own version. *************** *** 2233,2235 **** hba_getauthmethod(hbaPort *port) --- 2256,2969 ---- { check_hba(port); } + + + /* LDAP supports 10 currently, keep this well above the most any method needs */ + #define MAX_OPTIONS 12 + + /* + * Fill in suitable values to build a tuple representing the + * HbaLine provided + */ + static void + hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, bool hba_match_result) + { + ListCell *dbcell; + char buffer[NI_MAXHOST]; + int index = 0; + JsonbInState result; + JsonbValue v; + char *str; + int len; + + /* line_number */ + values[index] = Int32GetDatum(hba->linenumber); + + /* 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++; + memset(&result, 0, sizeof(JsonbInState)); + + result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL); + + if (list_length(hba->databases) != 0) + { + HbaToken *tok; + + foreach(dbcell, hba->databases) + { + tok = lfirst(dbcell); + add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false); + } + } + + result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL); + values[index] = PointerGetDatum(JsonbValueToJsonb(result.res)); + + /* user */ + index++; + memset(&result, 0, sizeof(JsonbInState)); + result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL); + + if (list_length(hba->roles) != 0) + { + HbaToken *tok; + + foreach(dbcell, hba->roles) + { + tok = lfirst(dbcell); + add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false); + } + } + + result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL); + values[index] = PointerGetDatum(JsonbValueToJsonb(result.res)); + + /* 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; + + /* hostname */ + index++; + if (hba->hostname) + values[index] = CStringGetTextDatum(hba->hostname); + else + nulls[index] = true; + + /* method */ + index++; + switch (hba->auth_method) + { + case uaReject: + values[index] = CStringGetTextDatum("reject"); + break; + case uaTrust: + values[index] = CStringGetTextDatum("trust"); + break; + case uaIdent: + values[index] = CStringGetTextDatum("ident"); + break; + case uaPassword: + values[index] = CStringGetTextDatum("password"); + break; + case uaMD5: + values[index] = CStringGetTextDatum("md5"); + break; + case uaGSS: + values[index] = CStringGetTextDatum("gss"); + break; + case uaSSPI: + values[index] = CStringGetTextDatum("sspi"); + break; + case uaPAM: + values[index] = CStringGetTextDatum("pam"); + break; + case uaLDAP: + values[index] = CStringGetTextDatum("ldap"); + break; + case uaCert: + values[index] = CStringGetTextDatum("cert"); + break; + case uaRADIUS: + values[index] = CStringGetTextDatum("radius"); + break; + case uaPeer: + values[index] = CStringGetTextDatum("peer"); + break; + default: + elog(ERROR, "unexpected authentication method in parsed HBA entry"); + break; + } + + /* options */ + index++; + memset(&result, 0, sizeof(JsonbInState)); + result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL); + + if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI) + { + if (hba->include_realm) + { + str = pstrdup("include_realm"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false); + } + + if (hba->krb_realm) + { + str = pstrdup("krb_realm"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->krb_realm), false, &result, TEXTOID, false); + } + } + + if (hba->usermap) + { + str = pstrdup("map"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->usermap), false, &result, TEXTOID, false); + } + + if (hba->auth_method == uaLDAP) + { + if (hba->ldapserver) + { + str = pstrdup("ldapserver"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapserver), false, &result, TEXTOID, false); + } + + if (hba->ldapport) + { + str = pstrdup("ldapport"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(Int32GetDatum(hba->ldapport), false, &result, INT4OID, false); + } + + if (hba->ldaptls) + { + str = pstrdup("ldaptls"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false); + } + + if (hba->ldapprefix) + { + str = pstrdup("ldapprefix"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapprefix), false, &result, TEXTOID, false); + } + + if (hba->ldapsuffix) + { + str = pstrdup("ldapsuffix"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapsuffix), false, &result, TEXTOID, false); + } + + if (hba->ldapbasedn) + { + str = pstrdup("ldapbasedn"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapbasedn), false, &result, TEXTOID, false); + } + + if (hba->ldapbinddn) + { + str = pstrdup("ldapbinddn"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapbinddn), false, &result, TEXTOID, false); + } + + if (hba->ldapbindpasswd) + { + str = pstrdup("ldapbindpasswd"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapbindpasswd), false, &result, TEXTOID, false); + } + + if (hba->ldapsearchattribute) + { + str = pstrdup("ldapsearchattribute"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->ldapsearchattribute), false, &result, TEXTOID, false); + } + + if (hba->ldapscope) + { + str = pstrdup("ldapscope"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(Int32GetDatum(hba->ldapscope), false, &result, INT4OID, false); + } + } + + if (hba->auth_method == uaRADIUS) + { + if (hba->radiusserver) + { + str = pstrdup("radiusserver"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->radiusserver), false, &result, TEXTOID, false); + } + + if (hba->radiussecret) + { + str = pstrdup("radiussecret"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->radiussecret), false, &result, TEXTOID, false); + } + + if (hba->radiusidentifier) + { + str = pstrdup("radiusidentifier"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(CStringGetTextDatum(hba->radiusidentifier), false, &result, TEXTOID, false); + } + + if (hba->radiusport) + { + str = pstrdup("radiusport"); + len = strlen(str); + + v.type = jbvString; + + v.val.string.len = len; + v.val.string.val = str; + + result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v); + add_jsonb(Int32GetDatum(hba->radiusport), false, &result, INT4OID, false); + } + } + + result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL); + values[index] = PointerGetDatum(JsonbValueToJsonb(result.res)); + + /* mode */ + index++; + if (hba_match_result) + values[index] = CStringGetTextDatum("matched"); + else + values[index] = CStringGetTextDatum("skipped"); + } + + static bool + pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage *addr, + char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode) + { + Oid roleid; + + if (ssl_inuse) + { + #ifdef USE_SSL + if (EnableSSL && hba->conntype != ctHostSSL) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non SSL connection type.", + hba->linenumber))); + return false; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("hostssl requires SSL to be turned on"), + errhint("Set ssl = on in postgresql.conf."))); + return false; + } + #else + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("hostssl is not supported by this build"), + errhint("Compile with --with-openssl to use SSL connections."))); + return false; + #endif + } + + /* Get the target role's OID. Note we do not error out for bad role. */ + roleid = get_role_oid(user, true); + + if (!check_db(database, user, roleid, hba->databases)) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching database.", + hba->linenumber))); + return false; + } + + if (!check_role(user, roleid, hba->roles)) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching role.", + hba->linenumber))); + return false; + } + + if (args_mode != TWO_ARGS_MODE) + { + /* Check IP address */ + switch (hba->ip_cmp_method) + { + case ipCmpMask: + if (hostname && hba->hostname) + { + if (!hostname_match(hostname, hba->hostname)) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching hostname.", + hba->linenumber))); + return false; + } + } + else + { + if (!check_ip((SockAddr *)addr, + (struct sockaddr *) & hba->addr, + (struct sockaddr *) & hba->mask)) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching IP.", + hba->linenumber))); + return false; + } + } + break; + case ipCmpAll: + break; + case ipCmpSameHost: + case ipCmpSameNet: + if (!check_same_host_or_net((SockAddr *)addr, + hba->ip_cmp_method)) + { + ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching SameHost/SameNet.", + hba->linenumber))); + return false; + } + break; + default: /* Not reachable case*/ + break; + } + } + + return true; + } + + + #define NUM_PG_HBA_CONF_ATTS 9 + + static void + pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage *addr, + char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi) + { + Tuplestorestate *tuple_store; + TupleDesc tupdesc; + ListCell *line; + MemoryContext old_cxt; + + /* + * 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_CONF_ATTS, false); + TupleDescInitEntry(tupdesc, (AttrNumber)1, "line_number", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)2, "type", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)3, "database", + JSONBOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)4, "user", + JSONBOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)5, "address", + INETOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)6, "hostname", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)7, "method", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)8, "options", + JSONBOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)9, "mode", + TEXTOID, -1, 0); + BlessTupleDesc(tupdesc); + + tuple_store = + tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, + false, work_mem); + + MemoryContextSwitchTo(old_cxt); + + /* + * Loop through the list and deparse each entry as it comes, storing it in + * the tuplestore. Any temporary memory allocations here live only for the + * function call lifetime. + */ + foreach(line, parsed_hba_lines) + { + HbaLine *hba = (HbaLine *)lfirst(line); + Datum values[NUM_PG_HBA_CONF_ATTS]; + bool nulls[NUM_PG_HBA_CONF_ATTS]; + HeapTuple tuple; + bool hba_match_result; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + CHECK_FOR_INTERRUPTS(); + + hba_match_result = pg_hba_match(hba, database, user, addr, hostname, ssl_inuse, args_mode); + + /* Get the next parsed hba line values */ + hba_getvalues_for_line(hba, values, nulls, hba_match_result); + + /* build a tuple */ + tuple = heap_form_tuple(tupdesc, values, nulls); + tuplestore_puttuple(tuple_store, tuple); + } + + rsi->setDesc = tupdesc; + rsi->setResult = tuple_store; + return; + } + + /* + * SQL-accessible SRF to return all the settings from the pg_hba.conf + * file. + */ + Datum + pg_hba_lookup_2args(PG_FUNCTION_ARGS) + { + return pg_hba_lookup(fcinfo); + } + + /* + * SQL-accessible SRF to return all the settings from the pg_hba.conf + * file. + */ + Datum + pg_hba_lookup_3args(PG_FUNCTION_ARGS) + { + return pg_hba_lookup(fcinfo); + } + + /* + * SQL-accessible SRF to return all the settings from the pg_hba.conf + * file. + */ + Datum + pg_hba_lookup(PG_FUNCTION_ARGS) + { + char *user; + char *database; + char *address; + char *hostname; + bool ssl_inuse = false; + struct sockaddr_storage addr; + hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of arguments */ + + /* + * 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. + */ + ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can view pg_hba.conf settings")))); + + 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"))); + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + (errmsg("database name is required to match pg_hba configuration entry")))); + else + database = 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 + user = TextDatumGetCString(PG_GETARG_DATUM(1)); + + if (PG_NARGS() != 2) + { + struct addrinfo *gai_result; + struct addrinfo hints; + int ret; + + args_mode = THREE_ARGS_MODE; + if (!PG_ARGISNULL(2)) + 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 = pg_getaddrinfo_all(address, NULL, &hints, &gai_result); + if (ret == 0 && gai_result) + memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen); + else if (ret == EAI_NONAME) + hostname = pstrdup(address); + else + { + ereport(ERROR, + (errmsg("invalid IP address \"%s\": %s", + address, gai_strerror(ret)))); + if (gai_result) + pg_freeaddrinfo_all(hints.ai_family, gai_result); + return false; + } + + pg_freeaddrinfo_all(hints.ai_family, gai_result); + + if (PG_NARGS() != 3) + { + args_mode = FOUR_ARGS_MODE; + if (!PG_ARGISNULL(3)) + ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3)); + } + } + + if (load_hba_failure) + ereport(WARNING, + (errmsg("There was some failure in reloading pg_hba.conf file. " + "The pg_hba.conf settings data may contains stale information"))); + + rsi->returnMode = SFRM_Materialize; + + pg_hba_lookup_internal(database, user, &addr, hostname, ssl_inuse, args_mode, rsi); + + PG_RETURN_NULL(); + } + + *** a/src/backend/tcop/postgres.c --- b/src/backend/tcop/postgres.c *************** *** 4005,4010 **** PostgresMain(int argc, char *argv[], --- 4005,4023 ---- { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); + + /* + * Reload authentication config files too to refresh + * pg_hba_conf view data. + */ + if (!load_hba()) + { + ereport(DEBUG1, + (errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information"))); + load_hba_failure = true; + } + + load_hba_failure = false; } /* *** a/src/backend/utils/adt/jsonb.c --- b/src/backend/utils/adt/jsonb.c *************** *** 28,39 **** #include "utils/syscache.h" #include "utils/typcache.h" - typedef struct JsonbInState - { - JsonbParseState *parseState; - JsonbValue *res; - } JsonbInState; - /* unlike with json categories, we need to treat json and jsonb differently */ typedef enum /* type categories for datum_to_jsonb */ { --- 28,33 ---- *************** *** 83,89 **** static void jsonb_categorize_type(Oid typoid, static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar); ! static void add_jsonb(Datum val, bool is_null, JsonbInState *result, Oid val_type, bool key_scalar); static JsonbParseState *clone_parse_state(JsonbParseState *state); static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent); --- 77,83 ---- static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar); ! void add_jsonb(Datum val, bool is_null, JsonbInState *result, Oid val_type, bool key_scalar); static JsonbParseState *clone_parse_state(JsonbParseState *state); static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent); *************** *** 1113,1119 **** composite_to_jsonb(Datum composite, JsonbInState *result) * lookups only once. */ ! static void add_jsonb(Datum val, bool is_null, JsonbInState *result, Oid val_type, bool key_scalar) { --- 1107,1113 ---- * lookups only once. */ ! void add_jsonb(Datum val, bool is_null, JsonbInState *result, Oid val_type, bool key_scalar) { *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 3079,3084 **** DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f --- 3079,3090 ---- 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 t t v u 2 0 2249 "25 25" "{25,25,23,25,3802,3802,869,25,25,3802,25}" "{i,i,o,o,o,o,o,o,o,o,o}" "{database,user,line_number,type,database,user,address,hostname,method,options,mode}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_)); + DESCR("view client authentication settings"); + DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 3 0 2249 "25 25 25" "{25,25,25,23,25,3802,3802,869,25,25,3802,25}" "{i,i,i,o,o,o,o,o,o,o,o,o}" "{database,user,address,line_number,type,database,user,address,hostname,method,options,mode}" _null_ _null_ pg_hba_lookup_3args _null_ _null_ _null_)); + DESCR("view client authentication settings"); + DATA(insert OID = 3999 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,3802,3802,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o}" "{database,user,address,ssl_inuse,line_number,type,database,user,address,hostname,method,options,mode}" _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_ )); *** a/src/include/libpq/hba.h --- b/src/include/libpq/hba.h *************** *** 96,101 **** typedef struct IdentLine --- 96,103 ---- /* kluge to avoid including libpq/libpq-be.h here */ typedef struct Port hbaPort; + extern bool load_hba_failure; + extern bool load_hba(void); extern bool load_ident(void); extern void hba_getauthmethod(hbaPort *port); *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS); --- 1123,1133 ---- extern Datum show_all_settings(PG_FUNCTION_ARGS); extern Datum show_all_file_settings(PG_FUNCTION_ARGS); + /* hba.c */ + extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS); + extern Datum pg_hba_lookup_3args(PG_FUNCTION_ARGS); + 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); *** a/src/include/utils/json.h --- b/src/include/utils/json.h *************** *** 43,49 **** extern Datum json_object(PG_FUNCTION_ARGS); extern Datum json_object_two_arg(PG_FUNCTION_ARGS); extern void escape_json(StringInfo buf, const char *str); - extern Datum json_typeof(PG_FUNCTION_ARGS); /* functions in jsonfuncs.c */ --- 43,48 ---- *** a/src/include/utils/jsonb.h --- b/src/include/utils/jsonb.h *************** *** 343,348 **** typedef struct JsonbIterator --- 343,354 ---- struct JsonbIterator *parent; } JsonbIterator; + typedef struct JsonbInState + { + JsonbParseState *parseState; + JsonbValue *res; + } JsonbInState; + /* I/O routines */ extern Datum jsonb_in(PG_FUNCTION_ARGS); extern Datum jsonb_out(PG_FUNCTION_ARGS); *************** *** 428,433 **** extern bool JsonbDeepContains(JsonbIterator **val, --- 434,441 ---- extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash); /* jsonb.c support functions */ + void add_jsonb(Datum val, bool is_null, JsonbInState *result, + Oid val_type, bool key_scalar); extern char *JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len); extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in, *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** *** 1327,1332 **** pg_group| SELECT pg_authid.rolname AS groname, --- 1327,1344 ---- WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); + pg_hba_conf| SELECT a.line_number, + a.type, + a.keyword_databases, + a.databases, + a.keyword_users, + a.users, + a.address, + a.compare_method, + a.hostname, + a.method, + a.options + FROM pg_hba_conf() a(line_number, type, keyword_databases, databases, keyword_users, users, address, compare_method, hostname, method, options); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname,