aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2022-03-29 10:15:48 +0900
committerMichael Paquier <michael@paquier.xyz>2022-03-29 10:15:48 +0900
commita2c84990bea7beadb599d02328190e2a763dcb86 (patch)
tree9ae2c552d668a4e4781ae0fac25a5a1ff29d5e17 /src
parent091a971bb59ca9751f32a4aa5aee969c5a915754 (diff)
downloadpostgresql-a2c84990bea7beadb599d02328190e2a763dcb86.tar.gz
postgresql-a2c84990bea7beadb599d02328190e2a763dcb86.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/system_views.sql6
-rw-r--r--src/backend/libpq/hba.c31
-rw-r--r--src/backend/utils/adt/hbafuncs.c136
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.dat6
-rw-r--r--src/include/libpq/hba.h1
-rw-r--r--src/test/regress/expected/rules.out6
-rw-r--r--src/test/regress/expected/sysviews.out8
-rw-r--r--src/test/regress/sql/sysviews.sql4
9 files changed, 185 insertions, 15 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 9570a53e7be..9eaa51df290 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -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();
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 673135144d4..f8393ca8ed4 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -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;
diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c
index f46cd935a1c..9fe7b62c9a0 100644
--- a/src/backend/utils/adt/hbafuncs.c
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -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();
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 05c677e8161..96649193d9c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202203272
+#define CATALOG_VERSION_NO 202203291
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index deb00307f6d..01e1dd4d6d1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -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 => '',
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 13ecb329f80..90036f7bcd3 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -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);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 92e1a2f6d8c..423b9b99fb6 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 92b48502dd3..579b861d84f 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -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
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 77e48ef7ccc..351e469c77b 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -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;