mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Add system view pg_ident_file_mappings
This view is similar to pg_hba_file_rules view, except that it is associated with the parsing of pg_ident.conf. Similarly to its cousin, this view is useful to check via SQL if changes planned in pg_ident.conf would work upon reload or restart, or to diagnose a previous failure. Bumps catalog version. Author: Julien Rouhaud Reviewed-by: Aleksander Alekseev, Michael Paquier Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya@jrouhaud
This commit is contained in:
@ -617,6 +617,12 @@ CREATE VIEW pg_hba_file_rules AS
|
||||
REVOKE ALL ON pg_hba_file_rules FROM PUBLIC;
|
||||
REVOKE EXECUTE ON FUNCTION pg_hba_file_rules() FROM PUBLIC;
|
||||
|
||||
CREATE VIEW pg_ident_file_mappings AS
|
||||
SELECT * FROM pg_ident_file_mappings() AS A;
|
||||
|
||||
REVOKE ALL ON pg_ident_file_mappings FROM PUBLIC;
|
||||
REVOKE EXECUTE ON FUNCTION pg_ident_file_mappings() FROM PUBLIC;
|
||||
|
||||
CREATE VIEW pg_timezone_abbrevs AS
|
||||
SELECT * FROM pg_timezone_abbrevs();
|
||||
|
||||
|
@ -887,25 +887,22 @@ do { \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Macros for handling pg_ident problems.
|
||||
* Much as above, but currently the message level is hardwired as LOG
|
||||
* and there is no provision for an err_msg string.
|
||||
* Macros for handling pg_ident problems, similar as above.
|
||||
*
|
||||
* IDENT_FIELD_ABSENT:
|
||||
* Log a message and exit the function if the given ident field ListCell is
|
||||
* not populated.
|
||||
* Reports when the given ident field ListCell is not populated.
|
||||
*
|
||||
* IDENT_MULTI_VALUE:
|
||||
* Log a message and exit the function if the given ident token List has more
|
||||
* than one element.
|
||||
* Reports when the given ident token List has more than one element.
|
||||
*/
|
||||
#define IDENT_FIELD_ABSENT(field) \
|
||||
do { \
|
||||
if (!field) { \
|
||||
ereport(LOG, \
|
||||
ereport(elevel, \
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
||||
errmsg("missing entry in file \"%s\" at end of line %d", \
|
||||
IdentFileName, line_num))); \
|
||||
*err_msg = psprintf("missing entry at end of line"); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
@ -913,11 +910,12 @@ do { \
|
||||
#define IDENT_MULTI_VALUE(tokens) \
|
||||
do { \
|
||||
if (tokens->length > 1) { \
|
||||
ereport(LOG, \
|
||||
ereport(elevel, \
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
|
||||
errmsg("multiple values in ident field"), \
|
||||
errcontext("line %d of configuration file \"%s\"", \
|
||||
line_num, IdentFileName))); \
|
||||
*err_msg = psprintf("multiple values in ident field"); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
@ -2306,7 +2304,8 @@ load_hba(void)
|
||||
* Parse one tokenised line from the ident config file and store the result in
|
||||
* an IdentLine structure.
|
||||
*
|
||||
* If parsing fails, log a message and return NULL.
|
||||
* If parsing fails, log a message at ereport level elevel, store an error
|
||||
* string in tok_line->err_msg and return NULL.
|
||||
*
|
||||
* If ident_user is a regular expression (ie. begins with a slash), it is
|
||||
* compiled and stored in IdentLine structure.
|
||||
@ -2315,10 +2314,11 @@ load_hba(void)
|
||||
* to have set a memory context that will be reset if this function returns
|
||||
* NULL.
|
||||
*/
|
||||
static IdentLine *
|
||||
parse_ident_line(TokenizedAuthLine *tok_line)
|
||||
IdentLine *
|
||||
parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
|
||||
{
|
||||
int line_num = tok_line->line_num;
|
||||
char **err_msg = &tok_line->err_msg;
|
||||
ListCell *field;
|
||||
List *tokens;
|
||||
AuthToken *token;
|
||||
@ -2372,11 +2372,14 @@ parse_ident_line(TokenizedAuthLine *tok_line)
|
||||
char errstr[100];
|
||||
|
||||
pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
|
||||
ereport(LOG,
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
||||
errmsg("invalid regular expression \"%s\": %s",
|
||||
parsedline->ident_user + 1, errstr)));
|
||||
|
||||
*err_msg = psprintf("invalid regular expression \"%s\": %s",
|
||||
parsedline->ident_user + 1, errstr);
|
||||
|
||||
pfree(wstr);
|
||||
return NULL;
|
||||
}
|
||||
@ -2627,7 +2630,7 @@ load_ident(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((newline = parse_ident_line(tok_line)) == NULL)
|
||||
if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
|
||||
{
|
||||
/* Parse error; remember there's trouble */
|
||||
ok = false;
|
||||
|
@ -28,6 +28,9 @@ static ArrayType *get_hba_options(HbaLine *hba);
|
||||
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
|
||||
int lineno, HbaLine *hba, const char *err_msg);
|
||||
static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
|
||||
static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
|
||||
int lineno, IdentLine *ident, const char *err_msg);
|
||||
static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
|
||||
|
||||
|
||||
/*
|
||||
@ -426,3 +429,136 @@ pg_hba_file_rules(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/* Number of columns in pg_ident_file_mappings view */
|
||||
#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5
|
||||
|
||||
/*
|
||||
* fill_ident_line: build one row of pg_ident_file_mappings view, add it to
|
||||
* tuplestore
|
||||
*
|
||||
* tuple_store: where to store data
|
||||
* tupdesc: tuple descriptor for the view
|
||||
* lineno: pg_ident.conf line number (must always be valid)
|
||||
* ident: parsed line data (can be NULL, in which case err_msg should be set)
|
||||
* err_msg: error message (NULL if none)
|
||||
*
|
||||
* Note: leaks memory, but we don't care since this is run in a short-lived
|
||||
* memory context.
|
||||
*/
|
||||
static void
|
||||
fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
|
||||
int lineno, IdentLine *ident, const char *err_msg)
|
||||
{
|
||||
Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
|
||||
bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
|
||||
HeapTuple tuple;
|
||||
int index;
|
||||
|
||||
Assert(tupdesc->natts == NUM_PG_IDENT_FILE_MAPPINGS_ATTS);
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, 0, sizeof(nulls));
|
||||
index = 0;
|
||||
|
||||
/* line_number */
|
||||
values[index++] = Int32GetDatum(lineno);
|
||||
|
||||
if (ident != NULL)
|
||||
{
|
||||
values[index++] = CStringGetTextDatum(ident->usermap);
|
||||
values[index++] = CStringGetTextDatum(ident->ident_user);
|
||||
values[index++] = CStringGetTextDatum(ident->pg_role);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no parsing result, so set relevant fields to nulls */
|
||||
memset(&nulls[1], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 2) * sizeof(bool));
|
||||
}
|
||||
|
||||
/* error */
|
||||
if (err_msg)
|
||||
values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = CStringGetTextDatum(err_msg);
|
||||
else
|
||||
nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = true;
|
||||
|
||||
tuple = heap_form_tuple(tupdesc, values, nulls);
|
||||
tuplestore_puttuple(tuple_store, tuple);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the pg_ident.conf file and fill the tuplestore with view records.
|
||||
*/
|
||||
static void
|
||||
fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
|
||||
{
|
||||
FILE *file;
|
||||
List *ident_lines = NIL;
|
||||
ListCell *line;
|
||||
MemoryContext linecxt;
|
||||
MemoryContext identcxt;
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/*
|
||||
* In the unlikely event that we can't open pg_ident.conf, we throw an
|
||||
* error, rather than trying to report it via some sort of view entry.
|
||||
* (Most other error conditions should result in a message in a view
|
||||
* entry.)
|
||||
*/
|
||||
file = AllocateFile(IdentFileName, "r");
|
||||
if (file == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not open usermap file \"%s\": %m",
|
||||
IdentFileName)));
|
||||
|
||||
linecxt = tokenize_auth_file(HbaFileName, file, &ident_lines, DEBUG3);
|
||||
FreeFile(file);
|
||||
|
||||
/* Now parse all the lines */
|
||||
identcxt = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"ident parser context",
|
||||
ALLOCSET_SMALL_SIZES);
|
||||
oldcxt = MemoryContextSwitchTo(identcxt);
|
||||
foreach(line, ident_lines)
|
||||
{
|
||||
TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
|
||||
IdentLine *identline = NULL;
|
||||
|
||||
/* don't parse lines that already have errors */
|
||||
if (tok_line->err_msg == NULL)
|
||||
identline = parse_ident_line(tok_line, DEBUG3);
|
||||
|
||||
fill_ident_line(tuple_store, tupdesc, tok_line->line_num, identline,
|
||||
tok_line->err_msg);
|
||||
}
|
||||
|
||||
/* Free tokenizer memory */
|
||||
MemoryContextDelete(linecxt);
|
||||
/* Free parse_ident_line memory */
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
MemoryContextDelete(identcxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL-accessible SRF to return all the entries in the pg_ident.conf file.
|
||||
*/
|
||||
Datum
|
||||
pg_ident_file_mappings(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ReturnSetInfo *rsi;
|
||||
|
||||
/*
|
||||
* Build tuplestore to hold the result rows. We must use the Materialize
|
||||
* mode to be safe against HBA file changes while the cursor is open. It's
|
||||
* also more efficient than having to look up our current position in the
|
||||
* parsed list every time.
|
||||
*/
|
||||
SetSingleFuncCall(fcinfo, 0);
|
||||
|
||||
/* Fill the tuplestore */
|
||||
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
fill_ident_view(rsi->setResult, rsi->setDesc);
|
||||
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 202203272
|
||||
#define CATALOG_VERSION_NO 202203291
|
||||
|
||||
#endif
|
||||
|
@ -6115,6 +6115,12 @@
|
||||
proargmodes => '{o,o,o,o,o,o,o,o,o}',
|
||||
proargnames => '{line_number,type,database,user_name,address,netmask,auth_method,options,error}',
|
||||
prosrc => 'pg_hba_file_rules' },
|
||||
{ oid => '9556', descr => 'show pg_ident.conf mappings',
|
||||
proname => 'pg_ident_file_mappings', prorows => '1000', proretset => 't',
|
||||
provolatile => 'v', prorettype => 'record', proargtypes => '',
|
||||
proallargtypes => '{int4,text,text,text,text}', proargmodes => '{o,o,o,o,o}',
|
||||
proargnames => '{line_number,map_name,sys_name,pg_username,error}',
|
||||
prosrc => 'pg_ident_file_mappings' },
|
||||
{ oid => '1371', descr => 'view system lock information',
|
||||
proname => 'pg_lock_status', prorows => '1000', proretset => 't',
|
||||
provolatile => 'v', prorettype => 'record', proargtypes => '',
|
||||
|
@ -171,6 +171,7 @@ extern int check_usermap(const char *usermap_name,
|
||||
const char *pg_role, const char *auth_user,
|
||||
bool case_sensitive);
|
||||
extern HbaLine *parse_hba_line(TokenizedAuthLine *tok_line, int elevel);
|
||||
extern IdentLine *parse_ident_line(TokenizedAuthLine *tok_line, int elevel);
|
||||
extern bool pg_isblank(const char c);
|
||||
extern MemoryContext tokenize_auth_file(const char *filename, FILE *file,
|
||||
List **tok_lines, int elevel);
|
||||
|
@ -1347,6 +1347,12 @@ pg_hba_file_rules| SELECT a.line_number,
|
||||
a.options,
|
||||
a.error
|
||||
FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error);
|
||||
pg_ident_file_mappings| SELECT a.line_number,
|
||||
a.map_name,
|
||||
a.sys_name,
|
||||
a.pg_username,
|
||||
a.error
|
||||
FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error);
|
||||
pg_indexes| SELECT n.nspname AS schemaname,
|
||||
c.relname AS tablename,
|
||||
i.relname AS indexname,
|
||||
|
@ -56,6 +56,14 @@ select count(*) > 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_e
|
||||
t | t
|
||||
(1 row)
|
||||
|
||||
-- There may be no rules, and there should be no errors.
|
||||
select count(*) >= 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
|
||||
from pg_ident_file_mappings;
|
||||
ok | no_err
|
||||
----+--------
|
||||
t | t
|
||||
(1 row)
|
||||
|
||||
-- There will surely be at least one active lock
|
||||
select count(*) > 0 as ok from pg_locks;
|
||||
ok
|
||||
|
@ -29,6 +29,10 @@ select count(*) >= 0 as ok from pg_file_settings;
|
||||
select count(*) > 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
|
||||
from pg_hba_file_rules;
|
||||
|
||||
-- There may be no rules, and there should be no errors.
|
||||
select count(*) >= 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
|
||||
from pg_ident_file_mappings;
|
||||
|
||||
-- There will surely be at least one active lock
|
||||
select count(*) > 0 as ok from pg_locks;
|
||||
|
||||
|
Reference in New Issue
Block a user