*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 16484,16489 **** SELECT collation for ('foo' COLLATE "de_DE"); --- 16484,16502 ---- 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 + *************** *** 16541,16546 **** SELECT set_config('log_statement_stats', 'off', false); --- 16554,16575 ---- + + 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,88 ---- bool quoted; } HbaToken; + /* 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, --- 109,119 ---- 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); + static bool pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname); + static void pg_hba_lookup_internal(char *user, char *database, char *address, + char *hostname, 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) --- 2247,2822 ---- { 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) + { + 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)); + } + + static bool + pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname) + { + Oid roleid; + + /* 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)) + return false; + + if (!check_role(user, roleid, hba->roles)) + return false; + + return true; + } + + #define NUM_PG_HBA_CONF_ATTS 8 + + static void + pg_hba_lookup_internal(char *user, char *database, char *address, + char *hostname, 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); + 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; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + CHECK_FOR_INTERRUPTS(); + + if (pg_hba_match(hba, user, database, address, hostname)) + { + /* Get the next parsed hba line values */ + hba_getvalues_for_line(hba, values, nulls); + + /* 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(PG_FUNCTION_ARGS) + { + char *user; + char *database; + char *address; + char *hostname; + + /* + * 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("user name is required to match pg_hba configuration entry")))); + else + user = TextDatumGetCString(PG_GETARG_DATUM(0)); + + if (PG_ARGISNULL(1)) + 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(1)); + + if (PG_NARGS() != 2) + { + if (!PG_ARGISNULL(2)) + address = TextDatumGetCString(PG_GETARG_DATUM(2)); + + if (!PG_ARGISNULL(3)) + hostname = TextDatumGetCString(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(user, database, address, hostname, rsi); + + PG_RETURN_NULL(); + } + + *** a/src/backend/tcop/postgres.c --- b/src/backend/tcop/postgres.c *************** *** 3998,4003 **** PostgresMain(int argc, char *argv[], --- 3998,4016 ---- { 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 *************** *** 36,47 **** #define DT_INFINITY "infinity" - 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 */ { --- 36,41 ---- *************** *** 82,88 **** 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); --- 76,82 ---- 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); *************** *** 1119,1125 **** 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) { --- 1113,1119 ---- * 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 *************** *** 3075,3080 **** DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f --- 3075,3084 ---- 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 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 2 0 2249 "25 25" "{25,25,23,25,3802,3802,869,25,25,3802}" "{i,i,o,o,o,o,o,o,o,o}" "{database,user,line_number,type,database,user,address,hostname,method,options}" _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 4 0 2249 "25 25 25 25" "{25,25,25,25,23,25,3802,3802,869,25,25,3802}" "{i,i,i,i,o,o,o,o,o,o,o,o}" "{database,user,address,hostname,line_number,type,database,user,address,hostname,method,options}" _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 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 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 *************** *** 1118,1123 **** extern Datum show_config_by_name_missing_ok(PG_FUNCTION_ARGS); --- 1118,1125 ---- 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); + extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS); + extern Datum pg_hba_lookup(PG_FUNCTION_ARGS); /* lockfuncs.c */ extern Datum pg_lock_status(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,