agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH 2/2] review 190+ messages / 4 participants [nested] [flat]
* [PATCH 2/2] review @ 2018-10-28 15:09 Tomas Vondra <tomas@2ndquadrant.com> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2018-10-28 15:09 UTC (permalink / raw) --- doc/src/sgml/ref/copy.sgml | 2 +- src/backend/commands/copy.c | 22 +++++++++++----------- src/backend/parser/gram.y | 2 +- src/include/nodes/parsenodes.h | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml index 8088e779b6..17bedf95cc 100644 --- a/doc/src/sgml/ref/copy.sgml +++ b/doc/src/sgml/ref/copy.sgml @@ -366,7 +366,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable </varlistentry> <varlistentry> - <term><literal>WHEN Clause</literal></term> + <term><literal>WHEN</literal></term> <listitem> <para> The optional <literal>WHEN</literal> clause has the general form diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 192a1c576b..8e87605135 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -803,7 +803,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, Relation rel; Oid relid; RawStmt *query = NULL; - Node *when_cluase= NULL; + Node *whenClause = NULL; /* * Disallow COPY to/from file or program except to users with the @@ -857,19 +857,19 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, NULL, false, false); rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); - if(stmt->whenClause) + if (stmt->whenClause) { /* add rte to column namespace */ addRTEtoQuery(pstate, rte, false, false, true); /* Transform the raw expression tree */ - when_cluase = transformExpr(pstate, stmt->whenClause, EXPR_KIND_OTHER); + whenClause = transformExpr(pstate, stmt->whenClause, EXPR_KIND_OTHER); /* Make sure it yields a boolean result. */ - when_cluase = coerce_to_boolean(pstate, when_cluase, "WHEN"); + whenClause = coerce_to_boolean(pstate, whenClause, "WHEN"); - when_cluase = (Node *) canonicalize_qual((Expr *) when_cluase, false); - when_cluase = (Node *) make_ands_implicit((Expr *) when_cluase); + whenClause = (Node *) canonicalize_qual((Expr *) whenClause, false); + whenClause = (Node *) make_ands_implicit((Expr *) whenClause); } tupDesc = RelationGetDescr(rel); @@ -1020,7 +1020,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program, NULL, stmt->attlist, stmt->options); - cstate->whenClause=when_cluase; + cstate->whenClause = whenClause; *processed = CopyFrom(cstate); /* copy from file to database */ EndCopyFrom(cstate); } @@ -2559,9 +2559,9 @@ CopyFrom(CopyState cstate) ExecSetupChildParentMapForLeaf(proute); } - if(cstate->whenClause ) + if (cstate->whenClause) cstate->qualexpr = ExecInitQual(castNode(List, cstate->whenClause), - &mtstate->ps); + &mtstate->ps); /* * It's more efficient to prepare a bunch of tuples for insertion, and @@ -2717,11 +2717,11 @@ next_record: slot = myslot; ExecStoreHeapTuple(tuple, slot, false); - if(cstate->whenClause) + if (cstate->whenClause) { econtext->ecxt_scantuple = myslot; if (!ExecQual(cstate->qualexpr, econtext)) - goto next_record; + goto next_record; } /* Determine the partition to heap_insert the tuple into */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0f316846b6..6063644850 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3180,7 +3180,7 @@ copy_generic_opt_arg_list: ; opt_when_clause: - WHEN a_expr { $$ = $2; } + WHEN '(' a_expr ')' { $$ = $3; } | /*EMPTY*/ { $$ = NULL; } ; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4d3a97f9bc..d9d41b5119 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1972,7 +1972,7 @@ typedef struct CopyStmt bool is_program; /* is 'filename' a program to popen? */ char *filename; /* filename, or NULL for STDIN/STDOUT */ List *options; /* List of DefElem nodes */ - Node *whenClause; /* qualifications */ + Node *whenClause; /* WHEN condition (or NULL) */ } CopyStmt; /* ---------------------- -- 2.13.6 --------------D20F32F15C6328E5A847507E Content-Type: text/x-patch; name="0001-rebased-patch.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-rebased-patch.patch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 2/2] review @ 2018-10-28 17:38 Tomas Vondra <tomas@2ndquadrant.com> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2018-10-28 17:38 UTC (permalink / raw) --- doc/src/sgml/ref/select.sgml | 6 ++-- src/backend/executor/nodeLimit.c | 52 +++++++++++++++++++-------------- src/backend/optimizer/plan/createplan.c | 25 +++++++--------- src/include/nodes/execnodes.h | 4 +-- src/include/nodes/parsenodes.h | 4 +-- src/include/nodes/plannodes.h | 2 +- src/include/nodes/relation.h | 2 +- 7 files changed, 49 insertions(+), 46 deletions(-) diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index c81ac04108..b649b1ca7a 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1408,9 +1408,9 @@ FETCH { FIRST | NEXT } [ <replaceable class="parameter">count</replaceable> ] { If <replaceable class="parameter">count</replaceable> is omitted in a <literal>FETCH</literal> clause, it defaults to 1. <literal>ROW</literal> - <literal>WITH TIES</literal> option is Used to return two or more rows - that tie for last place in the limit results set according to <literal>ORDER BY</literal> - clause (<literal>ORDER BY</literal> clause must be specified in this case). + <literal>WITH TIES</literal> option is used to return two or more rows + that tie for last place in the limit results set according to <literal>ORDER BY</literal> + clause (<literal>ORDER BY</literal> clause must be specified in this case). and <literal>ROWS</literal> as well as <literal>FIRST</literal> and <literal>NEXT</literal> are noise words that don't influence the effects of these clauses. diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index 93f8813972..724a8e09c1 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -132,7 +132,8 @@ ExecLimit(PlanState *pstate) * the state machine state to record having done so. */ if (!node->noCount && - node->position - node->offset >= node->count && node->limitOption == WITH_ONLY) + node->position - node->offset >= node->count && + node->limitOption == WITH_ONLY) { node->lstate = LIMIT_WINDOWEND; @@ -145,23 +146,24 @@ ExecLimit(PlanState *pstate) return NULL; } - else if (!node->noCount && - node->position - node->offset >= node->count && node->limitOption == WITH_TIES) + node->position - node->offset >= node->count && + node->limitOption == WITH_TIES) { /* - * Get next tuple from subplan, if any. - */ + * Get next tuple from subplan, if any. + */ slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) { node->lstate = LIMIT_SUBPLANEOF; return NULL; } + /* - * Test if the new tuple and the last tuple match. - * If so we return the tuple. - */ + * Test if the new tuple and the last tuple match. + * If so we return the tuple. + */ econtext->ecxt_innertuple = slot; econtext->ecxt_outertuple = node->last_slot; if (ExecQualAndReset(node->eqfunction, econtext)) @@ -184,22 +186,25 @@ ExecLimit(PlanState *pstate) return NULL; } - }else - /* - * Get next tuple from subplan, if any. - */ - slot = ExecProcNode(outerPlan); - if (TupIsNull(slot)) - { - node->lstate = LIMIT_SUBPLANEOF; - return NULL; } - if (node->limitOption == WITH_TIES) + else /* XXX So what exactly should be part of this else branch? */ { - ExecCopySlot(node->last_slot, slot); + /* + * Get next tuple from subplan, if any. + */ + slot = ExecProcNode(outerPlan); + if (TupIsNull(slot)) + { + node->lstate = LIMIT_SUBPLANEOF; + return NULL; + } + if (node->limitOption == WITH_TIES) + { + ExecCopySlot(node->last_slot, slot); + } + node->subSlot = slot; + node->position++; } - node->subSlot = slot; - node->position++; } else { @@ -356,7 +361,7 @@ recompute_limits(LimitState *node) * previous time we got a different result. */ if(node->limitOption == WITH_ONLY) - ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node)); + ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node)); } /* @@ -433,6 +438,9 @@ ExecInitLimit(Limit *node, EState *estate, int eflags) */ limitstate->ps.ps_ProjInfo = NULL; + /* + * Initialize the equality evaluation, to detect ties. + */ if (node->limitOption == WITH_TIES) { TupleDesc scanDesc; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 7e540849b4..62c05c7feb 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2435,6 +2435,9 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) { Limit *plan; Plan *subplan; + int numsortkeys = 0; + AttrNumber *sortColIdx = NULL; + Oid *sortOperators = NULL; /* Limit doesn't project, so tlist requirements pass through */ subplan = create_plan_recurse(root, best_path->subpath, flags); @@ -2442,9 +2445,6 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) if (best_path->limitOption == WITH_TIES) { Query *parse = root->parse; - int numsortkeys; - AttrNumber *sortColIdx; - Oid *sortOperators; ListCell *l; numsortkeys = list_length(parse->sortClause); @@ -2461,19 +2461,14 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) sortOperators[numsortkeys] = sortcl->eqop; numsortkeys++; } - plan = make_limit(subplan, - best_path->limitOffset, - best_path->limitCount, - best_path->limitOption, - numsortkeys, sortColIdx, - sortOperators); } - else - plan = make_limit(subplan, - best_path->limitOffset, - best_path->limitCount, - best_path->limitOption, - 0, NULL, NULL); + + plan = make_limit(subplan, + best_path->limitOffset, + best_path->limitCount, + best_path->limitOption, + numsortkeys, sortColIdx, + sortOperators); copy_generic_path_info(&plan->plan, (Path *) best_path); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 212820d886..6f15ce8ea4 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2242,7 +2242,7 @@ typedef struct LimitState PlanState ps; /* its first field is NodeTag */ ExprState *limitOffset; /* OFFSET parameter, or NULL if none */ ExprState *limitCount; /* COUNT parameter, or NULL if none */ - LimitOption limitOption; /* limit specification type */ + LimitOption limitOption; /* limit specification type */ int64 offset; /* current OFFSET value */ int64 count; /* current COUNT, if any */ bool noCount; /* if true, ignore count */ @@ -2250,7 +2250,7 @@ typedef struct LimitState int64 position; /* 1-based index of last tuple returned */ TupleTableSlot *subSlot; /* tuple last obtained from subplan */ ExprState *eqfunction; /* tuple equality qual in case of WITH TIES OPTION */ - TupleTableSlot *last_slot; + TupleTableSlot *last_slot; /* slot for evaluation of ties */ } LimitState; #endif /* EXECNODES_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ce10b55dcf..ce07a1a02d 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -159,7 +159,7 @@ typedef struct Query Node *limitOffset; /* # of result tuples to skip (int8 expr) */ Node *limitCount; /* # of result tuples to return (int8 expr) */ - LimitOption limitOption; /* limit type */ + LimitOption limitOption; /* LIMIT type [WITH TIES | WITH ONLY] */ List *rowMarks; /* a list of RowMarkClause's */ @@ -1574,7 +1574,7 @@ typedef struct SelectStmt List *sortClause; /* sort clause (a list of SortBy's) */ Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ - LimitOption limitOption; /* limit type */ + LimitOption limitOption; /* limit type */ List *lockingClause; /* FOR UPDATE (list of LockingClause's) */ WithClause *withClause; /* WITH clause */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 24b7e51a91..5e961a8f7b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -945,7 +945,7 @@ typedef struct Limit Plan plan; Node *limitOffset; /* OFFSET parameter, or NULL if none */ Node *limitCount; /* COUNT parameter, or NULL if none */ - LimitOption limitOption; /* LIMIT with ties or exact number */ + LimitOption limitOption; /* LIMIT with ties or exact number */ int numCols; /* number of columns to check for Similarity */ AttrNumber *uniqColIdx; /* their indexes in the target list */ Oid *uniqOperators; /* equality operators to compare with */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 2c7f4c71ea..f107d4f773 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1737,7 +1737,7 @@ typedef struct LimitPath Path *subpath; /* path representing input source */ Node *limitOffset; /* OFFSET parameter, or NULL if none */ Node *limitCount; /* COUNT parameter, or NULL if none */ - LimitOption limitOption; /* LIMIT with ties or exact number */ + LimitOption limitOption; /* LIMIT with ties or exact number */ } LimitPath; -- 2.13.6 --------------08FEC5268EA8E45E5EE90A4F Content-Type: text/x-patch; name="0001-rebased-patch.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-rebased-patch.patch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 1/2] review @ 2021-01-09 02:17 Tomas Vondra <tomas@2ndquadrant.com> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-01-09 02:17 UTC (permalink / raw) --- doc/src/sgml/mvcc.sgml | 20 +++++++++++--------- doc/src/sgml/ref/create_policy.sgml | 6 +++--- doc/src/sgml/ref/merge.sgml | 12 +++++++++--- src/backend/commands/explain.c | 2 +- src/backend/executor/execMerge.c | 5 ++++- src/backend/executor/nodeModifyTable.c | 3 ++- src/backend/optimizer/prep/preptlist.c | 6 ++++++ src/backend/parser/parse_agg.c | 1 + src/backend/replication/walsender.c | 4 ++-- src/backend/rewrite/rewriteHandler.c | 4 ++++ src/backend/utils/adt/varlena.c | 3 +++ src/include/nodes/parsenodes.h | 7 ++++--- 12 files changed, 50 insertions(+), 23 deletions(-) diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml index 9ec8474185..02c9f3fdea 100644 --- a/doc/src/sgml/mvcc.sgml +++ b/doc/src/sgml/mvcc.sgml @@ -429,22 +429,24 @@ COMMIT; with both <command>INSERT</command> and <command>UPDATE</command> subcommands looks similar to <command>INSERT</command> with an <literal>ON CONFLICT DO UPDATE</literal> clause but does not guarantee - that either <command>INSERT</command> and <command>UPDATE</command> will occur. + that either <command>INSERT</command> or <command>UPDATE</command> will occur. - If MERGE attempts an UPDATE or DELETE and the row is concurrently updated + /* XXX This is a very long and hard to understand sentence :-( */ + /* XXX Do we want to mention EvalPlanQual here? There's no explanation what it does in this file, so maybe elaborate what it does or leave that detail for a README? */ + If MERGE attempts an <command>UPDATE</command> or <command>DELETE</command> and the row is concurrently updated but the join condition still passes for the current target and the current - source tuple, then MERGE will behave the same as the UPDATE or DELETE commands + source tuple, then <command>MERGE</command> will behave the same as the <command>UPDATE</command> or <command>DELETE</command> commands and perform its action on the latest version of the row, using standard - EvalPlanQual. MERGE actions can be conditional, so conditions must be + EvalPlanQual. <command>MERGE</command> actions can be conditional, so conditions must be re-evaluated on the latest row, starting from the first action. On the other hand, if the row is concurrently updated or deleted so that - the join condition fails, then MERGE will execute a NOT MATCHED action, if it - exists and the AND WHEN qual evaluates to true. + the join condition fails, then <command>MERGE</command> will execute a <literal>NOT MATCHED</literal> action, if it + exists and the <literal>AND WHEN</literal> qual evaluates to true. - If MERGE attempts an INSERT and a unique index is present and a duplicate - row is concurrently inserted then a uniqueness violation is raised. MERGE - does not attempt to avoid the ERROR by attempting an UPDATE. + If <command>MERGE</command> attempts an <command>INSERT</command> and a unique index is present and a duplicate + row is concurrently inserted then a uniqueness violation is raised. <command>MERGE</command> + does not attempt to avoid the <literal>ERROR</literal> by attempting an <command>UPDATE</command>. </para> <para> diff --git a/doc/src/sgml/ref/create_policy.sgml b/doc/src/sgml/ref/create_policy.sgml index ad20230c58..0a64674f2d 100644 --- a/doc/src/sgml/ref/create_policy.sgml +++ b/doc/src/sgml/ref/create_policy.sgml @@ -97,9 +97,9 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable <para> No separate policy exists for <command>MERGE</command>. Instead policies - defined for <literal>SELECT</literal>, <literal>INSERT</literal>, - <literal>UPDATE</literal> and <literal>DELETE</literal> are applied - while executing MERGE, depending on the actions that are activated. + defined for <command>SELECT</command>, <command>INSERT</command>, + <command>UPDATE</command> and <command>DELETE</command> are applied + while executing <command>MERGE</command>, depending on the actions that are activated. </para> </refsect1> diff --git a/doc/src/sgml/ref/merge.sgml b/doc/src/sgml/ref/merge.sgml index b2a9f67cfa..c2901b0d58 100644 --- a/doc/src/sgml/ref/merge.sgml +++ b/doc/src/sgml/ref/merge.sgml @@ -98,7 +98,7 @@ DELETE </para> <para> - There is no MERGE privilege. + There is no <literal>MERGE</literal> privilege. You must have the <literal>UPDATE</literal> privilege on the column(s) of the <replaceable class="parameter">target_table_name</replaceable> referred to in the <literal>SET</literal> clause @@ -217,6 +217,7 @@ DELETE At least one <literal>WHEN</literal> clause is required. </para> <para> + /* XXX Do we need to repeat the same thing for WHEN MATCHED and WHEN NOT MATCHED? Could this say that it applies to both? */ If the <literal>WHEN</literal> clause specifies <literal>WHEN MATCHED</literal> and the candidate change row matches a row in the <replaceable class="parameter">target_table_name</replaceable> @@ -242,6 +243,7 @@ DELETE clause will be activated and the corresponding action will occur for that row. The expression may not contain functions that possibly performs writes to the database. + /* XXX Does it mean that it has to be marked as STABLE, or that a write crashes the DB? */ </para> <para> A condition on a <literal>WHEN MATCHED</literal> clause can refer to columns @@ -379,6 +381,7 @@ DELETE <para> An expression to assign to the column. The expression can use the old values of this and other columns in the table. + /* XXX Which table? Source or target, or both? What if it's NOT MATCHED? */ </para> </listitem> </varlistentry> @@ -492,6 +495,7 @@ MERGE <replaceable class="parameter">total-count</replaceable> In summary, statement triggers for an event type (say, INSERT) will be fired whenever we <emphasis>specify</emphasis> an action of that kind. Row-level triggers will fire only for the one event type <emphasis>activated</emphasis>. + /* XXX What does "activated" mean? Maybe "executed" would be better? */ So a <command>MERGE</command> might fire statement triggers for both <command>UPDATE</command> and <command>INSERT</command>, even though only <command>UPDATE</command> row triggers were fired. @@ -504,6 +508,8 @@ MERGE <replaceable class="parameter">total-count</replaceable> rows will be used to modify the target row, later attempts to modify will cause an error. This can also occur if row triggers make changes to the target table which are then subsequently modified by <command>MERGE</command>. + /* XXX Not sure the preceding sentence makes sense. What does the 'which' refer to? Row triggers, changes, or what? */ + /* XXX It seems the rule is that INSERT actions are <literal> while INSERT statements (in SQL sense) are <command>. But this mixes that up? */ If the repeated action is an <command>INSERT</command> this will cause a uniqueness violation while a repeated <command>UPDATE</command> or <command>DELETE</command> will cause a cardinality violation; the latter behavior @@ -554,7 +560,7 @@ MERGE <replaceable class="parameter">total-count</replaceable> <title>Examples</title> <para> - Perform maintenance on CustomerAccounts based upon new Transactions. + Perform maintenance on <literal>CustomerAccounts</literal> based upon new <literal>Transactions</literal>. <programlisting> MERGE CustomerAccount CA @@ -599,7 +605,7 @@ WHEN MATCHED THEN DELETE; </programlisting> - The wine_stock_changes table might be, for example, a temporary table + The <literal>wine_stock_changes</literal> table might be, for example, a temporary table recently loaded into the database. </para> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index fbaf50c258..f914a00e9f 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3690,7 +3690,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors, break; case CMD_MERGE: operation = "Merge"; - foperation = "Foreign Merge"; + foperation = "Foreign Merge"; /* XXX Doesn't the commit message say foreign tables are not yet supported? */ break; default: operation = "???"; diff --git a/src/backend/executor/execMerge.c b/src/backend/executor/execMerge.c index 0e245e1361..a7492a6c4b 100644 --- a/src/backend/executor/execMerge.c +++ b/src/backend/executor/execMerge.c @@ -105,7 +105,7 @@ ExecMerge(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, * A concurrent update can: * * 1. modify the target tuple so that it no longer satisfies the - * additional quals attached to the current WHEN MATCHED action OR + * additional quals attached to the current WHEN MATCHED action * * In this case, we are still dealing with a WHEN MATCHED case, but we * should recheck the list of WHEN MATCHED actions and choose the first @@ -118,6 +118,9 @@ ExecMerge(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, * tuple, so we now instead find a qualifying WHEN NOT MATCHED action to * execute. * + * XXX Hmmm, what if the updated tuple would now match one that was + * considered NOT MATCHED so far? + * * A concurrent delete, changes a WHEN MATCHED case to WHEN NOT MATCHED. * * ExecMergeMatched takes care of following the update chain and diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index c0a97eba91..9c14709e47 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2124,7 +2124,6 @@ ExecModifyTable(PlanState *pstate) { /* advance to next subplan if any */ node->mt_whichplan++; - if (node->mt_whichplan < node->mt_nplans) { resultRelInfo++; @@ -2169,6 +2168,8 @@ ExecModifyTable(PlanState *pstate) EvalPlanQualSetSlot(&node->mt_epqstate, planSlot); slot = planSlot; + /* XXX Wouldn't it be "nicer" to handle MERGE in the switch, together + * with the other MT operations? This seems a bit out of place. */ if (operation == CMD_MERGE) { ExecMerge(node, resultRelInfo, estate, slot, junkfilter); diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 8848e9a03f..b2543f6814 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -117,6 +117,10 @@ preprocess_targetlist(PlannerInfo *root) tlist = expand_targetlist(tlist, command_type, result_relation, target_relation); + /* + * For MERGE we need to handle the target list for the target relation, + * and also target list for each action (only INSERT/UPDATE matter). + */ if (command_type == CMD_MERGE) { ListCell *l; @@ -343,6 +347,8 @@ expand_targetlist(List *tlist, int command_type, * generate a NULL for dropped columns (we want to drop any old * values). * + * XXX Should this explain why MERGE has the same logic as UPDATE? + * * When generating a NULL constant for a dropped column, we label * it INT4 (any other guaranteed-to-exist datatype would do as * well). We can't label it with the dropped column's datatype diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index f7c152beb4..5d49c8c37e 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -436,6 +436,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) break; case EXPR_KIND_MERGE_WHEN_AND: if (isAgg) + /* XXX "WHEN AND" seems rather strange. ... */ err = _("aggregate functions are not allowed in WHEN AND conditions"); else err = _("grouping operations are not allowed in WHEN AND conditions"); diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index d59f4fde95..d50543b1ba 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1198,6 +1198,7 @@ StartLogicalReplication(StartReplicationCmd *cmd) /* Also update the sent position status in shared memory */ SpinLockAcquire(&MyWalSnd->mutex); + /* XXX Huh? Why does MERGE patch change walsender? */ MyWalSnd->sentPtr = MyReplicationSlot->data.confirmed_flush; SpinLockRelease(&MyWalSnd->mutex); @@ -2930,8 +2931,7 @@ WalSndDone(WalSndSendDataCallback send_data) replicatedPtr = XLogRecPtrIsInvalid(MyWalSnd->flush) ? MyWalSnd->write : MyWalSnd->flush; - if (WalSndCaughtUp && - sentPtr == replicatedPtr && + if (WalSndCaughtUp && sentPtr == replicatedPtr && !pq_is_send_pending()) { QueryCompletion qc; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index e2706c181c..80ae946d17 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1774,6 +1774,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) } } + /* * matchLocks - * match the list of locks and returns the matching rules @@ -3946,6 +3947,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events) /* * XXX MERGE doesn't support write rules because they would violate * the SQL Standard spec and would be unclear how they should work. + * + * XXX So does't support means 'ignores'? Should that either fail + * or at least print some warning? */ if (event == CMD_MERGE) product_queries = NIL; diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 0281351dcf..7a768a7b5b 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -2312,6 +2312,7 @@ varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup) int result; bool arg1_match; + /* XXX Huh? Why is this in MERGE patch? */ if (len1 < 0 || len2 < 0) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), @@ -2505,6 +2506,7 @@ varstr_abbrev_convert(Datum original, SortSupport ssup) memset(pres, 0, sizeof(Datum)); len = VARSIZE_ANY_EXHDR(authoritative); + /* XXX Huh? Why is this in MERGE patch? */ if (len < 0) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), @@ -3260,6 +3262,7 @@ bytea_catenate(bytea *t1, bytea *t2) len1 = VARSIZE_ANY_EXHDR(t1); len2 = VARSIZE_ANY_EXHDR(t2); + /* XXX Huh? Why is this in MERGE patch? */ if (len1 < 0 || len2 < 0) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 3cba38044e..08fea9b8f7 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -172,6 +172,7 @@ typedef struct Query List *withCheckOptions; /* a list of WithCheckOption's (added * during rewrite) */ + /* XXX Why not mergeTragetRelation? */ int mergeTarget_relation; List *mergeSourceTargetList; List *mergeActionList; /* list of actions for MERGE (only) */ @@ -1581,11 +1582,11 @@ typedef struct UpdateStmt typedef struct MergeStmt { NodeTag type; - RangeVar *relation; /* target relation to merge into */ + RangeVar *relation; /* target relation to merge into */ Node *source_relation; /* source relation */ - Node *join_condition; /* join condition between source and target */ + Node *join_condition; /* join condition between source and target */ List *mergeWhenClauses; /* list of MergeWhenClause(es) */ - WithClause *withClause; /* WITH clause */ + WithClause *withClause; /* WITH clause */ } MergeStmt; typedef struct MergeWhenClause -- 2.26.2 --------------B1AA4DC5EAE9BAF8B146244A Content-Type: text/plain; charset=UTF-8; name="0002-fix-valgrind-failure.txt" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0002-fix-valgrind-failure.txt" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 2/2] review @ 2021-03-09 20:09 Tomas Vondra <tomas.vondra@postgresql.org> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-03-09 20:09 UTC (permalink / raw) --- src/backend/optimizer/path/costsize.c | 77 ++++++++++++++++++++------- src/backend/optimizer/path/pathkeys.c | 14 ++++- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1639258aaf..f55a4f20e5 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1755,6 +1755,8 @@ cost_recursive_union(Path *runion, Path *nrterm, Path *rterm) * is_fake_var * Workaround for generate_append_tlist() which generates fake Vars with * varno == 0, that will cause a fail of estimate_num_group() call + * + * XXX Ummm, why would estimate_num_group fail with this? */ static bool is_fake_var(Expr *expr) @@ -1828,27 +1830,53 @@ get_width_cost_multiplier(PlannerInfo *root, Expr *expr) * compute_cpu_sort_cost * compute CPU cost of sort (i.e. in-memory) * + * The main thing we need to calculate to estimate sort CPU costs is the number + * of calls to the comparator functions. The difficulty is that for multi-column + * sorts there may be different data types involved (for some of which the calls + * may be much more expensive). Furthermore, the columns may have very different + * number of distinct values - the higher the number, the fewer comparisons will + * be needed for the following columns. + * + * The algoritm is incremental - we add pathkeys one by one, and at each step we + * estimate the number of necessary comparisons (based on the number of distinct + * groups in the current pathkey prefix and the new pathkey), and the comparison + * costs (which is data type specific). + * + * Estimation of the number of comparisons is based on ideas from two sources: + * + * 1) "Algorithms" (course), Robert Sedgewick, Kevin Wayne [https://algs4.cs.princeton.edu/home/] + * + * 2) "Quicksort Is Optimal For Many Equal Keys" (paper), Sebastian Wild, + * arXiv:1608.04906v4 [cs.DS] 1 Nov 2017. [https://arxiv.org/abs/1608.04906] + * + * In term of that paper, let N - number of tuples, Xi - number of tuples with + * key Ki, then the estimate of number of comparisons is: + * + * log(N! / (X1! * X2! * ..)) ~ sum(Xi * log(N/Xi)) + * + * In our case all Xi are the same because now we don't have any estimation of + * group sizes, we have only know the estimate of number of groups (distinct + * values). In that case, formula becomes: + * + * N * log(NumberOfGroups) + * + * For multi-column sorts we need to estimate the number of comparisons for + * each individual column - for example with columns (c1, c2, ..., ck) we + * can estimate that number of comparions on ck is roughly + * + * ncomparisons(c1, c2, ..., ck) / ncomparisons(c1, c2, ..., c(k-1)) + * + * Let k be a column number, Gk - number of groups defined by k columns, and Fk + * the cost of the comparison is + * + * N * sum( Fk * log(Gk) ) + * + * Note: We also consider column witdth, not just the comparator cost. + * * NOTE: some callers currently pass NIL for pathkeys because they * can't conveniently supply the sort keys. In this case, it will fallback to * simple comparison cost estimate. - * - * Estimation algorithm is based on ideas from course Algorithms, - * Robert Sedgewick, Kevin Wayne, https://algs4.cs.princeton.edu/home/ and paper - * "Quicksort Is Optimal For Many Equal Keys", Sebastian Wild, - * arXiv:1608.04906v4 [cs.DS] 1 Nov 2017. - * - * In term of that papers, let N - number of tuples, Xi - number of tuples with - * key Ki, then estimation is: - * log(N! / (X1! * X2! * ..)) ~ sum(Xi * log(N/Xi)) - * In our case all Xi are the same because now we don't have an estimation of - * group sizes, we have only estimation of number of groups. In this case, - * formula becomes: N * log(NumberOfGroups). Next, to support correct estimation - * of multi-column sort we need separately compute each column, so, let k is a - * column number, Gk - number of groups defined by k columns: - * N * sum( Fk * log(Gk) ) - * Fk is a function costs (including width) for k columns. */ - static Cost compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, Cost comparison_cost, double tuples, double output_tuples, @@ -1862,7 +1890,7 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, bool has_fake_var = false; int i = 0; Oid prev_datatype = InvalidOid; - Cost funcCost = 0.; + Cost funcCost = 0.0; List *cache_varinfos = NIL; /* fallback if pathkeys is unknown */ @@ -1873,6 +1901,10 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, * a total number of tuple comparisons of N log2 K; but the constant * factor is a bit higher than for quicksort. Tweak it so that the * cost curve is continuous at the crossover point. + * + * XXX I suppose the "quicksort factor" references to 1.5 at the end + * of this function, but I'm not sure. I suggest we introduce some simple + * constants for that, instead of magic values. */ output_tuples = (heapSort) ? 2.0 * output_tuples : tuples; per_tuple_cost += 2.0 * cpu_operator_cost * LOG2(output_tuples); @@ -1888,7 +1920,6 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, * - per column comparison function cost * - we try to compute needed number of comparison per column */ - foreach(lc, pathkeys) { PathKey *pathkey = (PathKey*)lfirst(lc); @@ -1952,6 +1983,11 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, * Don't use DEFAULT_NUM_DISTINCT because it used for only one * column while here we try to estimate number of groups over * set of columns. + * + * XXX Perhaps this should use DEFAULT_NUM_DISTINCT at least to + * limit the calculated values, somehow? + * + * XXX What's the logic of the following formula? */ nGroups = ceil(2.0 + sqrt(tuples) * list_length(pathkeyExprs) / list_length(pathkeys)); @@ -1968,6 +2004,7 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, { if (tuplesPerPrevGroup < output_tuples) /* comparing only inside output_tuples */ + /* XXX why not to use the same multiplier (1.5)? */ correctedNGroups = ceil(2.0 * output_tuples / (tuplesPerPrevGroup / nGroups)); else @@ -1993,7 +2030,7 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys, tuplesPerPrevGroup = ceil(1.5 * tuplesPerPrevGroup / nGroups); /* - * We could skip all followed columns for cost estimation, because we + * We could skip all following columns for cost estimation, because we * believe that tuples are unique by set ot previous columns */ if (tuplesPerPrevGroup <= 1.0) diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 7beb32488a..b092c3e055 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -515,10 +515,19 @@ pathkey_sort_cost_comparator(const void *_a, const void *_b) return 0; return 1; } + /* * Order tail of list of group pathkeys by uniqueness descendetly. It allows to * speedup sorting. Returns newly allocated lists, old ones stay untouched. * n_preordered defines a head of list which order should be prevented. + * + * XXX But we're not generating this only based on uniqueness (that's a bad + * term anyway, because we're using ndistinct estimates, not uniqueness). + * We're also using the comparator cost to calculate the expected sort cost, + * and optimize that. + * + * XXX This should explain how we generate the values - all permutations for + * up to 4 values, etc. */ void get_cheapest_group_keys_order(PlannerInfo *root, double nrows, @@ -597,8 +606,9 @@ get_cheapest_group_keys_order(PlannerInfo *root, double nrows, else { /* - * Since v13 list_free() can clean list elements so for original list not to be modified it should be copied to - * a new one which can then be cleaned safely if needed. + * Since v13 list_free() can clean list elements so for original list + * not to be modified it should be copied to a new one which can then + * be cleaned safely if needed. */ new_group_pathkeys = list_copy(*group_pathkeys); nToPermute = nFreeKeys; -- 2.29.2 --------------A955BFDEDE369DCD57A45C5A-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 02/10] review @ 2021-03-16 16:29 Tomas Vondra <tomas.vondra@postgresql.org> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-03-16 16:29 UTC (permalink / raw) --- src/backend/optimizer/path/allpaths.c | 2 ++ src/backend/optimizer/plan/initsplan.c | 9 ++++++++- src/backend/optimizer/util/plancat.c | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 37b4223adb..fc1a3a68a2 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1065,8 +1065,10 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* Whole row is not null, so must be same for child */ childrel->notnullattrs = bms_add_member(childrel->notnullattrs, attno - FirstLowInvalidHeapAttributeNumber); + /* XXX shouldn't this be a continue, instead of a break? */ break; } + /* XXX isn't this missing 'else'? */ if (attno < 0 ) /* no need to translate system column */ child_attno = attno; diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index d27167dc76..4ef876cf7b 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -829,7 +829,14 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, { Node *qual = (Node *) lfirst(l); - /* Set the not null info now */ + /* Set the not null info now + * + * XXX Why now? Why is this the right place to do this? Does it need + * to happen before distribute_qual_to_rels, for example? + * + * XXX Not clear to me why this looks at non-nullable vars? Shouldn't + * we already have the bitmap built from atnums (from get_relation_info)? + */ ListCell *lc; List *non_nullable_vars = find_nonnullable_vars(qual); foreach(lc, non_nullable_vars) diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index eebabcfccf..cd703e41ba 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -481,6 +481,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) set_relation_partition_info(root, rel, relation); + /* + * Build information about which attributes are marked as NOT NULL. + */ Assert(rel->notnullattrs == NULL); for(i = 0; i < relation->rd_att->natts; i++) { -- 2.30.2 --------------F495F6B18672582269F00FF9 Content-Type: text/x-patch; charset=UTF-8; name="0003-Introduce-UniqueKey-attributes-on-RelOptInf-20210317.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0003-Introduce-UniqueKey-attributes-on-RelOptInf-20210317.pa"; filename*1="tch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 04/10] review @ 2021-03-16 18:26 Tomas Vondra <tomas.vondra@postgresql.org> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-03-16 18:26 UTC (permalink / raw) --- src/backend/nodes/copyfuncs.c | 4 + src/backend/nodes/makefuncs.c | 1 - src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 2 + src/backend/optimizer/path/README.uniquekey | 285 +++--- src/backend/optimizer/path/allpaths.c | 10 +- src/backend/optimizer/path/joinrels.c | 7 + src/backend/optimizer/path/uniquekeys.c | 906 ++++++++++++++------ src/backend/optimizer/plan/planner.c | 10 + src/backend/optimizer/prep/prepunion.c | 1 + src/backend/optimizer/util/inherit.c | 1 + 11 files changed, 863 insertions(+), 365 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 75c1c5e824..9d832ddc03 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2296,6 +2296,9 @@ _copyPathKey(const PathKey *from) return newnode; } +/* + * _copyUniqueKey + */ static UniqueKey * _copyUniqueKey(const UniqueKey *from) { @@ -2306,6 +2309,7 @@ _copyUniqueKey(const UniqueKey *from) return newnode; } + /* * _copyRestrictInfo */ diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 40415d0f5b..e156c9cdf8 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -816,7 +816,6 @@ makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols) return v; } - /* * makeUniqueKey */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 44154cde6a..13905e6037 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2460,6 +2460,7 @@ static void _outUniqueKey(StringInfo str, const UniqueKey *node) { WRITE_NODE_TYPE("UNIQUEKEY"); + WRITE_NODE_FIELD(exprs); WRITE_BOOL_FIELD(multi_nullvals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b3e212bf1c..8830c8df99 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -496,8 +496,10 @@ static UniqueKey * _readUniqueKey(void) { READ_LOCALS(UniqueKey); + READ_NODE_FIELD(exprs); READ_BOOL_FIELD(multi_nullvals); + READ_DONE(); } diff --git a/src/backend/optimizer/path/README.uniquekey b/src/backend/optimizer/path/README.uniquekey index 5eac761995..31cdb5ed65 100644 --- a/src/backend/optimizer/path/README.uniquekey +++ b/src/backend/optimizer/path/README.uniquekey @@ -1,131 +1,208 @@ -1. What is UniqueKey? -We can think UniqueKey is a set of exprs for a RelOptInfo, which we are insure -that doesn't yields same result among all the rows. The simplest UniqueKey -format is primary key. +review comments: +XXX Maybe move this to src/backend/optimizer/README.uniquekey? +XXX multi_nullvals name seems a bit weird +XXX no info about populate_distinctrel_uniquekeys, populate_grouprel_uniquekeys, populate_unionrel_uniquekeys +----- +src/backend/optimizer/path/README.uniquekey + +UniqueKey +========= + +UniqueKey is a set of exprs for a RelOptInfo, which are known to have unique +values on all the rows in the relation. A trivial example is a primary key +defined on a relation - each attributes of the constraint is a unique key. + +We can use this knowledge to perform optimization in a number of places. Some +of the optimizations are fairly obvious, others are less so: + +1. remove DISTINCT node if the clause is unique +2. remove aggregation if group by clause is unique +3. remove_useless_joins +4. reduce_semianti_joins +5. Index Skip Scan (WIP) +6. Aggregation Push-Down without 2-phase aggregation if the join can't + duplicate the aggregated rows. (WIP) + -However we define the UnqiueKey as below. +UniqueKey struct +---------------- -typedef struct UniqueKey -{ +A UnqiueKey is represented by the following struct: + + typedef struct UniqueKey + { NodeTag type; List *exprs; bool multi_nullvals; -} UniqueKey; - -exprs is a list of exprs which is unique on current RelOptInfo. exprs = NIL -is a special case of UniqueKey, which means there is only one row in that -relation.it has a stronger semantic than others. like SELECT uk FROM t; uk is -normal unique key and may have different values. SELECT colx FROM t WHERE uk = -const. colx is unique AND we have only 1 value. This field can used for -innerrel_is_unique. this logic is handled specially in add_uniquekey_for_onerow -function. - -multi_nullvals: true means multi null values may exist in these exprs, so the -uniqueness is not guaranteed in this case. This field is necessary for -remove_useless_join & reduce_unique_semijoins where we don't mind these -duplicated NULL values. It is set to true for 2 cases. One is a unique key -from a unique index but the related column is nullable. The other one is for -outer join. see populate_joinrel_uniquekeys for detail. - - -The UniqueKey can be used at the following cases at least: -1. remove_useless_joins. -2. reduce_semianti_joins -3. remove distinct node if distinct clause is unique. -4. remove aggnode if group by clause is unique. -5. Index Skip Scan (WIP) -6. Aggregation Push Down without 2 phase aggregation if the join can't - duplicated the aggregated rows. (work in progress feature) - -2. How is it maintained? - -We have a set of populate_xxx_unqiuekeys functions to maintain the uniquekey on -various cases. xxx includes baserel, joinrel, partitionedrel, distinctrel, -groupedrel, unionrel. and we also need to convert the uniquekey from subquery -to outer relation, which is what convert_subquery_uniquekeys does. - -1. The first part is about baserel. We handled 3 cases. suppose we have Unique -Index on (a, b). - -1. SELECT a, b FROM t. UniqueKey (a, b) -2. SELECT a FROM t WHERE b = 1; UniqueKey (a) -3. SELECT .. FROM t WHERE a = 1 AND b = 1; UniqueKey (NIL). onerow case, every - column is Unique. - -2. The next part is joinrel, this part is most error-prone, we simplified the rules -like below: -1. If the relation's UniqueKey can't be duplicated after join, then is will be - still valid for the join rel. The function we used here is - innerrel_keeps_unique. The basic idea is innerrel.any_col = outer.uk. - -2. If the UnqiueKey can't keep valid via the rule 1, the combination of the - UniqueKey from both sides are valid for sure. We can prove this as: if the - unique exprs from rel1 is duplicated by rel2, the duplicated rows must - contains different unique exprs from rel2. - -More considerations about onerow: -1. If relation with one row and it can't be duplicated, it is still possible - contains mulit_nullvas after outer join. -2. If the either UniqueKey can be duplicated after join, the can get one row - only when both side is one row AND there is no outer join. -3. Whenever the onerow UniqueKey is not a valid any more, we need to convert one - row UniqueKey to normal unique key since we don't store exprs for one-row - relation. get_exprs_from_uniquekeys will be used here. - - -More considerations about multi_nullvals after join: + } UniqueKey; + +exprs is a list of exprs which are know to be unique on current RelOptInfo. + +exprs = NIL is a special case, meaning there is only one row in the relation. +This has has a stronger semantic than others. Consider for example + + SELECT uk FROM t + +where 'uk' is a unique key. This guarantees uniqueness, but there may be mamy +rows in the relation. On the other hand, consider this query + + SELECT colx FROM t WHERE uk = const + +In this case we know there's only a single matching row (thanks to a condition +on the unique key), which in turn guarantees uniqueness of the colx value, even +if there is no constraint on the column itself. + +This knowledge is used in innerrel_is_unique, and is handled as a special case +in add_uniquekey_for_onerow. + + +The multi_nullvals field tracks whether the expressions may contain multiple +NULL values. This can happen for example when the unique key is derived from +a unique index with nullable columns, or because of outer joins (which may add +NULL values to a known-unique list - see populate_joinrel_uniquekeys). + +In this case uniqueness is not guaranteed, but we can still use the information +in places places where NULL values are harmless - when removing useless joins, +reducing semijoins, and so on. + + +How is it maintained? +--------------------- + +Deducing the unique keys depends on the type of the relation - for each case +there's a separate "populate" function: + + +populate_baserel_uniquekeys +--------------------------- + +There are three cases, all assuming there's a unique index (e.g. on (a,b)): + +1. SELECT a, b FROM t => UniqueKey (a, b) +2. SELECT a FROM t WHERE b = 1 => UniqueKey (a) +3. SELECT .. FROM t WHERE a = 1 AND b = 1; => UniqueKey (NIL) + +The last query is the "one row" case, in which case every column is Unique. + + +populate_joinrel_uniquekeys +--------------------------- + +For joins, deducing the unique keys may be fairly complex and error-prone. +We've simplified the rules like this: + +1. If the UniqueKey on an input relation can't be duplicated by the join, then +it will be valid for the join rel. A typical example is a join like this: + + inner_rel.any_col = outer_rel.unique_key + +The function used to detect this is innerrel_keeps_unique. + +2. Any combination of unique keys on each side of the join is a unique key +for the join relation. This can be proved by contradiction - assume we have +unique key on either side of the join - uk1 and uk2. If the values in uk1 get +duplicated by the join with uk2 (by matching the row to multiple rows), the +duplicated rows must have different values in the uk2. + +We can also leverage information about the "one row" case: + +1. If one of the input relations is known to have a single row, and the join +can't duplicate the row (e.g. semi/anti join), we can keep the unique keys. +It may however contain multi_nullvals after an outer join. + +XXX Not sure I understand the original logic/wording :-( + +2. If either UniqueKey can be duplicated after a join, there can be only one +row only when both sides are "one row" AND there is no outer join. + +XXX Why the restriction on not allowing outer joins? + +3. Whenever the one row UniqueKey is not a valid any more, we need to convert +UniqueKey to normal unique key since we don't store exprs for one-row relation. +This is done by get_exprs_from_uniquekeys. + +The join case needs to be careful about multi_nullvals too: + 1. If the original UnqiueKey has multi_nullvals, the final UniqueKey will have - mulit_nullvals in any case. -2. If a unique key doesn't allow mulit_nullvals, after some outer join, it - allows some outer join. +mulit_nullvals in any case too. + +2. If the original unique key doesn't allow multi_nullvals, the unique key for +the join relation may allow multi_nullvals after an outer join. + + +subqueries +---------- + +It's necessary to "translate" unique keys between a subquery and the outer rels, +which is what convert_subquery_uniquekeys does. This does almost exactly what +convert_subquery_pathkeys does for pathkeys. It keeps only unique keys matching +Vars in the outer relation. The relationship between outerrel.Var and +subquery.exprs is built from outerel->subroot->processed_tlist. -3. When we comes to subquery, we need to convert_subquery_unqiuekeys just like -convert_subquery_pathkeys. Only the UniqueKey insides subquery is referenced as -a Var in outer relation will be reused. The relationship between the outerrel.Var -and subquery.exprs is built with outerel->subroot->processed_tlist. +set-returning functions +------------------------ +As for the SRF functions, it will break the uniqueness of uniquekey, However it +is handled in adjust_paths_for_srfs, which happens after the query_planner. So +we will maintain the UniqueKey until there and reset it to NIL at that place. -4. As for the SRF functions, it will break the uniqueness of uniquekey, However it -is handled in adjust_paths_for_srfs, which happens after the query_planner. so -we will maintain the UniqueKey until there and reset it to NIL at that -places. This can't help on distinct/group by elimination cases but probably help -in some other cases, like reduce_unqiue_semijoins/remove_useless_joins and it is -semantic correctly. +This can't help on distinct/group by elimination cases but probably help in some +other cases, like reduce_unqiue_semijoins/remove_useless_joins and it is correct. -5. As for inherit table, we first main the UnqiueKey on childrel as well. But for -partitioned table we need to maintain 2 different kinds of -UnqiueKey. 1). UniqueKey on the parent relation 2). UniqueKey on child -relation for partition wise query. +populate_partitionedrel_uniquekeys +---------------------------------- + +As for inherit table, we first build the UnqiueKey on childrel as well. But for +partitioned table we need to maintain two different kinds of UniqueKey: + +1) UniqueKey on the parent relation + +2) UniqueKey on child + +This is needed because a unique key from the partition may not be be unique key +on the partitioned table. + Example: -CREATE TABLE p (a int not null, b int not null) partition by list (a); + +CREATE TABLE p (a INT NOT NULL, b INT NOT NULL) PARTITION BY LIST (a); + CREATE TABLE p0 partition of p for values in (1); CREATE TABLE p1 partition of p for values in (2); -create unique index p0_b on p0(b); -create unique index p1_b on p1(b); +CREATE UNIQUE INDEX p0_b ON p0(b); +CREATE UNIQUE INDEX p1_b ON p1(b); -Now b is only unique on partition level, so the distinct can't be removed on -the following cases. SELECT DISTINCT b FROM p; +SELECT DISTINCT b FROM p; -Another example is SELECT DISTINCT a, b FROM p WHERE a = 1; Since only one -partition is chosen, the UniqueKey on child relation is same as the UniqueKey on -parent relation. +Now "b" is only unique on partition level, but the two partitions may contain +duplicate values for the "b" column (with different values in "a"). That means +the DISTINCT clause can't be removed. -Another usage of UniqueKey on partition level is it be helpful for -partition-wise join. +Now consider: -As for the UniqueKey on parent table level, it comes with 2 different ways, -1). the UniqueKey is also derived in UniqueKey index, but the index must be same -in all the related children relations and the unique index must contains -Partition Key in it. Example: +SELECT DISTINCT a, b FROM p WHERE a = 1 + +In this case, the optimizer eliminates all partitions except for one, so that +the UniqueKey is valid for the parent relation too. + +UniqueKey at a partition level is useful for partition-wise join too. + +XXX Explain why is it useful? + +A UniqueKey from a partition can be transferred to the parent relation, in two +cases. A trivial case is if there's a single child relation (e.g. thanks to +partition elimination). In that case all unique keys on the child relation are +automatically valid for the parent relation. If there are multiple relations, +the unique key must be defived from an index present in all partitions, and the +index has to include the partition key. + +Example: CREATE UNIQUE INDEX p_ab ON p(a, b); -- where a is the partition key. -- Query SELECT a, b FROM p; the (a, b) is a UniqueKey of p. -2). If the parent relation has only one childrel, the UniqueKey on childrel is - the UniqueKey on parent as well. diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 66bf6f19f7..a801707eaa 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -581,7 +581,8 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* * Now that we've marked which partial indexes are suitable, we can now - * build the relation's unique keys. + * build the relation's unique keys. We need to do it in this order, + * so that we don't deduce unique keys from inapplicable partial indexes. */ populate_baserel_uniquekeys(root, rel, rel->indexlist); @@ -1305,6 +1306,12 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Add paths to the append relation. */ add_paths_to_append_rel(root, rel, live_childrels); + + /* + * XXX Maybe move the check into populate populate_partitionedrel_uniquekeys? + * XXX What if it's append rel (but not partitioned one), but there's only one + * child relation? We could still deduce unique keys, no? + */ if (IS_PARTITIONED_REL(rel)) populate_partitionedrel_uniquekeys(root, rel, live_childrels); } @@ -2314,6 +2321,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, pathkeys, required_outer)); } + /* Convert subpath's unique keys to outer representation */ convert_subquery_uniquekeys(root, rel, sub_final_rel); /* If outer rel allows parallelism, do same for partial paths. */ diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 7271f044ec..eefba449d6 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -925,6 +925,13 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, /* Apply partitionwise join technique, if possible. */ try_partitionwise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist); + /* + * Determine which of the unique keys from input relations are applicable + * for the join result. + * + * XXX We do this after trying the partitionwise join, because that may allow + * using additional unique keys. + */ populate_joinrel_uniquekeys(root, joinrel, rel1, rel2, restrictlist, sjinfo->jointype); } diff --git a/src/backend/optimizer/path/uniquekeys.c b/src/backend/optimizer/path/uniquekeys.c index 77ed2b2eff..114e8334f5 100644 --- a/src/backend/optimizer/path/uniquekeys.c +++ b/src/backend/optimizer/path/uniquekeys.c @@ -36,7 +36,7 @@ typedef struct UniqueKeyContextData bool useful; } *UniqueKeyContext; -static List *initililze_uniquecontext_for_joinrel(RelOptInfo *inputrel); +static List *initialize_uniquecontext_for_joinrel(RelOptInfo *inputrel); static bool innerrel_keeps_unique(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, @@ -80,8 +80,20 @@ static void add_uniquekey_from_sortgroups(PlannerInfo *root, /* * populate_baserel_uniquekeys - * Populate 'baserel' uniquekeys list by looking at the rel's unique index - * and baserestrictinfo + * Build list of unique keys for the base relation. + * + * Inspects unique indexes defined on the relation and determines what + * unique keys are valid. Partial indexes are considered too, if the + * predicate is valid. + * + * This also inspects baserestrictinfo, because we need to determine + * which opclass families are interesting when inspecting indexes. If we + * have a unique index and distinct clause with a mismatching opclasses, + * we should not use that. + * + * XXX Why does this look at baserestrictinfo? + * + * XXX What about collations? */ void populate_baserel_uniquekeys(PlannerInfo *root, @@ -99,22 +111,48 @@ populate_baserel_uniquekeys(PlannerInfo *root, Assert(baserel->rtekind == RTE_RELATION); + if (!indexlist) + return; + + /* + * Determine which unique indexes to use to build the unique keys. + * We have to skip partial with predicates not matched by the query, + * and unique indexes that are not immediately enforced. + * + * XXX Do we actually skip indexes that are not immediate? + * XXX What about hypothetical indexes? + */ foreach(lc, indexlist) { IndexOptInfo *ind = (IndexOptInfo *) lfirst(lc); + if (!ind->unique || !ind->immediate || (ind->indpred != NIL && !ind->predOK)) continue; + matched_uniq_indexes = lappend(matched_uniq_indexes, ind); } + /* If there are not applicable unique indexes, we're done. */ if (matched_uniq_indexes == NIL) return; - /* Check which attrs is used in baserel->reltarget */ - pull_varattnos((Node *)baserel->reltarget->exprs, baserel->relid, &used_attrs); + /* + * Determine which attrs are referenced in baserel->reltarget. To use the + * unique key info, we need all the columns - a unique index on (a,b) may + * not be unique on (a). If a column is missing in reltarget, the nodes + * above can't possibly use it, and we can just ignore any matching index. + */ + pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid, &used_attrs); - /* Check which attrno is used at a mergeable const filter */ + /* + * Check which attrno is used at a mergeable const filter + * + * XXX This is not lookint att attrno at all, maybe obsolete comment? + * + * Seems the primary purpose of this is determining which opclass + * families to use when matching unique indexes in the next loop? + */ foreach(lc, baserel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); @@ -122,6 +160,10 @@ populate_baserel_uniquekeys(PlannerInfo *root, if (rinfo->mergeopfamilies == NIL) continue; + /* + * XXX What if bms_is_empty is true for both left_relids/right_relids? + * Or what if it's false in both cases? + */ if (bms_is_empty(rinfo->left_relids)) { const_exprs = lappend(const_exprs, get_rightop(rinfo->clause)); @@ -136,40 +178,69 @@ populate_baserel_uniquekeys(PlannerInfo *root, expr_opfamilies = lappend(expr_opfamilies, rinfo->mergeopfamilies); } + /* + * Now try to match unique indexes to attributes in reltarget, and to + * merge operator families. The index may be on the right attributes, + * but if it's not matching the opfamily it's useless. + * + * XXX Can we have multiple baserestrictinfo for the same attribute, + * with different opfamilies? Probably not. + */ foreach(lc, matched_uniq_indexes) { - bool multi_nullvals, useful; - List *exprs = get_exprs_from_uniqueindex(lfirst_node(IndexOptInfo, lc), - const_exprs, - expr_opfamilies, - used_attrs, - &useful, - &multi_nullvals); - if (useful) + bool multi_nullvals, + useful; + + IndexOptInfo *index_info = (IndexOptInfo *) lfirst_node(IndexOptInfo, lc); + + List *exprs = get_exprs_from_uniqueindex(index_info, + const_exprs, + expr_opfamilies, + used_attrs, + &useful, + &multi_nullvals); + + if (!useful) + continue; + + /* + * All the columns in Unique Index matched with a restrictinfo, so + * that we know there's just a one row in the result. If we find + * such index, we're done - we discard all other unique keys and + * keep just this special one. In principle, this is a stronger + * guarantee, because all subsets of one row are still unique. + * + * XXX Is it correct to just return? Doesn't that prevent some + * optimizations that might be possible with the other keys? + */ + if (exprs == NIL) { - if (exprs == NIL) - { - /* All the columns in Unique Index matched with a restrictinfo */ - add_uniquekey_for_onerow(baserel); - return; - } - baserel->uniquekeys = lappend(baserel->uniquekeys, - makeUniqueKey(exprs, multi_nullvals)); + /* discards all previous uniquekeys */ + add_uniquekey_for_onerow(baserel); + return; } + + baserel->uniquekeys = lappend(baserel->uniquekeys, + makeUniqueKey(exprs, multi_nullvals)); } } /* * populate_partitionedrel_uniquekeys - * The UniqueKey on partitionrel comes from 2 cases: - * 1). Only one partition is involved in this query, the unique key can be - * copied to parent rel from childrel. - * 2). There are some unique index which includes partition key and exists - * in all the related partitions. - * We never mind rule 2 if we hit rule 1. + * Determine unique keys for a partitioned relation. + * + * Inspects unique keys for all partitions and derives unique keys that + * are valid for the whole partitioned table. There are two basic cases: + * + * 1) There's only one remaining partition (thanks to pruning all other + * partitions). In this case all the unique keys from the partition are + * trivially valid for the partitioned table. + * + * 2) All the partitions have the same unique index (on the same set of + * columns), and the index includes the partition key. This ensures the + * combination of values is unique for the whole partitioned table. */ - void populate_partitionedrel_uniquekeys(PlannerInfo *root, RelOptInfo *rel, @@ -180,110 +251,181 @@ populate_partitionedrel_uniquekeys(PlannerInfo *root, RelOptInfo *childrel; bool is_first = true; + /* XXX What about append rels? At least for the one-child case? */ Assert(IS_PARTITIONED_REL(rel)); + /* if there are no child relations, we're done. */ if (childrels == NIL) return; /* - * If there is only one partition used in this query, the UniqueKey in childrel is - * still valid in parent level, but we need convert the format from child expr to - * parent expr. + * If there is only one partition used in this query, the UniqueKey for + * a child relation is still valid for the parent level. We need to + * convert the format from child expr to parent expr. */ if (list_length(childrels) == 1) { - /* Check for Rule 1 */ RelOptInfo *childrel = linitial_node(RelOptInfo, childrels); ListCell *lc; + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + + /* If the partition has a single row, so does the parent. */ if (relation_is_onerow(childrel)) { add_uniquekey_for_onerow(rel); return; } + /* + * Inspect the unique keys one by one, try reusing them for the + * parent relation. + * + * FIXME This needs more work to handle expressions and not just + * simple Vars. + */ foreach(lc, childrel->uniquekeys) { + ListCell *lc2; + List *parent_exprs = NIL; + bool can_reuse = true; + UniqueKey *ukey = lfirst_node(UniqueKey, lc); AppendRelInfo *appinfo = find_appinfo_by_child(root, childrel->relid); - List *parent_exprs = NIL; - bool can_reuse = true; - ListCell *lc2; + + /* + * XXX Not sure what exactly we do here. Surely we deal with + * expressions at child/parent level elsewhere? Can't we just + * copy the code from there? + */ foreach(lc2, ukey->exprs) { - Var *var = (Var *)lfirst(lc2); + Var *var = (Var *) lfirst(lc2); + /* - * If the expr comes from a expression, it is hard to build the expression - * in parent so ignore that case for now. + * XXX For now this only supports simple Var expressions, + * so if there's a more complex expression we'll not copy + * the unique key to the parent. */ if(!IsA(var, Var)) { can_reuse = false; break; } + /* Convert it to parent var */ - parent_exprs = lappend(parent_exprs, find_parent_var(appinfo, var)); + parent_exprs = lappend(parent_exprs, + find_parent_var(appinfo, var)); } - if (can_reuse) - rel->uniquekeys = lappend(rel->uniquekeys, - makeUniqueKey(parent_exprs, - ukey->multi_nullvals)); + + /* ignore unique keys with complex expressions */ + if (!can_reuse) + continue; + + rel->uniquekeys = lappend(rel->uniquekeys, + makeUniqueKey(parent_exprs, + ukey->multi_nullvals)); } + + return; } - else + + /* + * A parent with multiple child relations. We only care about indexes that + * are in all child relations, so we loop through indexes on the first one + * and check that they exist in the other child relations too. + */ + + childrel = linitial_node(RelOptInfo, childrels); + foreach(lc, childrel->indexlist) { - /* Check for rule 2 */ - childrel = linitial_node(RelOptInfo, childrels); - foreach(lc, childrel->indexlist) - { - IndexOptInfo *ind = lfirst(lc); - IndexOptInfo *modified_index; - if (!ind->unique || !ind->immediate || - (ind->indpred != NIL && !ind->predOK)) - continue; + IndexOptInfo *ind = lfirst(lc); + IndexOptInfo *modified_index; - /* - * During simple_copy_indexinfo_to_parent, we need to convert var from - * child var to parent var, index on expression is too complex to handle. - * so ignore it for now. - */ - if (ind->indexprs != NIL) - continue; + /* + * Ignore indexes that are not unique, immediately enforced. Partial + * indexes with mismatched predicate are useless too. + */ + if (!ind->unique || !ind->immediate || + (ind->indpred != NIL && !ind->predOK)) + continue; - modified_index = simple_copy_indexinfo_to_parent(root, rel, ind); - /* - * If the unique index doesn't contain partkey, then it is unique - * on this partition only, so it is useless for us. - */ - if (!index_constains_partkey(rel, modified_index)) - continue; + /* + * During simple_copy_indexinfo_to_parent, we need to convert var from + * child var to parent var, index on expression is too complex to handle. + * so ignore it for now. + * + * FIXME We should support indexes on expressions. + */ + if (ind->indexprs != NIL) + continue; - global_uniq_indexlist = lappend(global_uniq_indexlist, modified_index); - } + /* + * Adopt the index definition for the parent. + * + * XXX This seems rather weird. We're constructing "artificial" index + * for the partitioned table (kinda like a global index). Can't we + * just have some simpler struct representing it? + */ + modified_index = simple_copy_indexinfo_to_parent(root, rel, ind); + + /* + * If the unique index doesn't contain partkey, then it is unique + * on this partition only, so it is useless for us. + * + * XXX Can't we do this check before simple_copy_indexinfo_to_parent? + */ + if (!index_constains_partkey(rel, modified_index)) + continue; - if (global_uniq_indexlist != NIL) + global_uniq_indexlist = lappend(global_uniq_indexlist, modified_index); + } + + /* if there are no applicable unique indexes, we're done */ + if (!global_uniq_indexlist) + return; + + /* + * We iterate over the child relations first, and inspect the unique + * indexes for each hild, because this way we can stop early if we + * happen to eliminate all the unique indexes. + */ + foreach(lc, childrels) + { + RelOptInfo *child = lfirst(lc); + + /* skip the first index, which is where we got the list from */ + if (is_first) { - foreach(lc, childrels) - { - RelOptInfo *child = lfirst(lc); - if (is_first) - { - is_first = false; - continue; - } - adjust_partition_unique_indexlist(root, rel, child, &global_uniq_indexlist); - } - /* Now we have a list of unique index which are exactly same on all childrels, - * Set the UniqueKey just like it is non-partition table - */ - populate_baserel_uniquekeys(root, rel, global_uniq_indexlist); + is_first = false; + continue; } + + /* match the unique keys to indexes on this child */ + adjust_partition_unique_indexlist(root, rel, child, &global_uniq_indexlist); + + /* + * If we have eliminated all unique indexes, no point in looking at + * the remaining child relations. + */ + if (!global_uniq_indexlist) + break; } + + /* Now we have a list of unique index which are exactly same on all child + * relations. Set the UniqueKey just like it is non-partition table. + */ + populate_baserel_uniquekeys(root, rel, global_uniq_indexlist); } /* * populate_distinctrel_uniquekeys + * Update unique keys for relation produced by DISTINCT. + * + * We can keep all unique keys from the input relations, because DISTINCT + * can only remove rows - it can't duplicate them. Also, the DISTINCT clause + * itself is a unique key, so add that. */ void populate_distinctrel_uniquekeys(PlannerInfo *root, @@ -292,11 +434,13 @@ populate_distinctrel_uniquekeys(PlannerInfo *root, { /* The unique key before the distinct is still valid. */ distinctrel->uniquekeys = list_copy(inputrel->uniquekeys); + add_uniquekey_from_sortgroups(root, distinctrel, root->parse->distinctClause); } /* * populate_grouprel_uniquekeys + * */ void populate_grouprel_uniquekeys(PlannerInfo *root, @@ -305,54 +449,76 @@ populate_grouprel_uniquekeys(PlannerInfo *root, { Query *parse = root->parse; - bool input_ukey_added = false; ListCell *lc; + /* + * XXX Is this actually valid, before checking fro grouping sets? + * The grouping sets may produce duplicate row even with just a single + * input row, I think. + */ if (relation_is_onerow(inputrel)) { add_uniquekey_for_onerow(grouprel); return; } + + /* + * Bail out if there are grouping sets. + * + * XXX Could we maybe inspect the grouping sets and determine if this + * generates distinct combinations? In some cases that's clearly not + * the case (rollup, cube), but for some simple cases it might. + */ if (parse->groupingSets) return; - /* A Normal group by without grouping set. */ - if (parse->groupClause) + /* It has aggregation but without a group by, so only one row returned */ + if (!parse->groupClause) + add_uniquekey_for_onerow(grouprel); + + /* + * A regular group by, without grouping sets. + * + * Obviously, the whole group clause determines a unique key. But if + * there are smaller unique keys on the input rel, we prefer those + * because those are more flexible. If (a,b) is unique, (a,b,c) is + * unique too. Only when there are no such smaller unique keys, we + * add the unique key derived from the group clause. + */ + foreach(lc, inputrel->uniquekeys) { + UniqueKey *ukey = lfirst_node(UniqueKey, lc); + /* - * Current even the groupby clause is Unique already, but if query has aggref - * We have to create grouprel still. To keep the UnqiueKey short, we will check - * the UniqueKey of input_rel still valid, if so we reuse it. + * Ignore unique keys on the input that are not subset of the + * group clause. We can't use incomplete unique keys. */ - foreach(lc, inputrel->uniquekeys) - { - UniqueKey *ukey = lfirst_node(UniqueKey, lc); - if (list_is_subset(ukey->exprs, grouprel->reltarget->exprs)) - { - grouprel->uniquekeys = lappend(grouprel->uniquekeys, - ukey); - input_ukey_added = true; - } - } - if (!input_ukey_added) - /* - * group by clause must be a super-set of grouprel->reltarget->exprs except the - * aggregation expr, so if such exprs is unique already, no bother to generate - * new uniquekey for group by exprs. - */ - add_uniquekey_from_sortgroups(root, - grouprel, - root->parse->groupClause); + if (!list_is_subset(ukey->exprs, grouprel->reltarget->exprs)) + continue; + + grouprel->uniquekeys = lappend(grouprel->uniquekeys, ukey); } - else - /* It has aggregation but without a group by, so only one row returned */ - add_uniquekey_for_onerow(grouprel); + + /* + * Group clause must be a super-set of of grouprel->reltarget->exprs, + * except for the aggregation expressions. So if we found a smaller + * unique key on the input relation, don't bother adding a unique key + * for the group clause. + */ + if (!grouprel->uniquekeys) + add_uniquekey_from_sortgroups(root, + grouprel, + root->parse->groupClause); } /* * simple_copy_uniquekeys - * Using a function for the one-line code makes us easy to check where we simply - * copied the uniquekey. + * Copy yhe unique keys between relations. + * + * Using a function for the one-line code makes us easy to check where we + * simply copied the uniquekey. + * + * XXX Seems like an overkill, not sure what's the purpose? */ void simple_copy_uniquekeys(RelOptInfo *oldrel, @@ -362,24 +528,27 @@ simple_copy_uniquekeys(RelOptInfo *oldrel, } /* - * populate_unionrel_uniquekeys + * populate_unionrel_uniquekeys + * Determine unique keys for UNION relation. + * + * XXX Does this need to care about UNION vs. UNION ALL? At least in the + * one-row code path? */ void populate_unionrel_uniquekeys(PlannerInfo *root, - RelOptInfo *unionrel) + RelOptInfo *unionrel) { - ListCell *lc; - List *exprs = NIL; + ListCell *lc; + List *exprs = NIL; Assert(unionrel->uniquekeys == NIL); + /* XXX Why are we copying the expressions? */ foreach(lc, unionrel->reltarget->exprs) - { exprs = lappend(exprs, lfirst(lc)); - } + /* SQL: select union select; is valid, we need to handle it here. */ if (exprs == NIL) - /* SQL: select union select; is valid, we need to handle it here. */ add_uniquekey_for_onerow(unionrel); else unionrel->uniquekeys = lappend(unionrel->uniquekeys, @@ -389,6 +558,7 @@ populate_unionrel_uniquekeys(PlannerInfo *root, /* * populate_joinrel_uniquekeys + * Determine unique keys for a join relation. * * populate uniquekeys for joinrel. We will check each relation to see if its * UniqueKey is still valid via innerrel_keeps_unique, if so, we add it to @@ -404,70 +574,99 @@ populate_joinrel_uniquekeys(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype) { - ListCell *lc, *lc2; - List *clause_list = NIL; - List *outerrel_ukey_ctx; - List *innerrel_ukey_ctx; - bool inner_onerow, outer_onerow; - bool mergejoin_allowed; - - /* Care about the outerrel relation only for SEMI/ANTI join */ + ListCell *lc, + *lc2; + List *clause_list = NIL; + List *outerrel_ukey_ctx; + List *innerrel_ukey_ctx; + bool inner_onerow, + outer_onerow; + bool mergejoin_allowed; + + /* For SEMI/ANTI join, we care only about the outerrel unique keys. */ if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) { foreach(lc, outerrel->uniquekeys) { UniqueKey *uniquekey = lfirst_node(UniqueKey, lc); + + /* Keep the unique key if it's included in the joinrel. */ if (list_is_subset(uniquekey->exprs, joinrel->reltarget->exprs)) joinrel->uniquekeys = lappend(joinrel->uniquekeys, uniquekey); } + return; } + /* XXX What about JOIN_RIGHT? */ Assert(jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_INNER); - /* Fast path */ + /* + * For regular joins, we need to combine unique keys from both sides + * of the join, to get a new unique key for the join relation. So if + * either side does not have a unique key, bail out. + */ if (innerrel->uniquekeys == NIL || outerrel->uniquekeys == NIL) return; + /* XXX maybe move to the if blocks? Not needed outside. */ inner_onerow = relation_is_onerow(innerrel); outer_onerow = relation_is_onerow(outerrel); - outerrel_ukey_ctx = initililze_uniquecontext_for_joinrel(outerrel); - innerrel_ukey_ctx = initililze_uniquecontext_for_joinrel(innerrel); + outerrel_ukey_ctx = initialize_uniquecontext_for_joinrel(outerrel); + innerrel_ukey_ctx = initialize_uniquecontext_for_joinrel(innerrel); - clause_list = select_mergejoin_clauses(root, joinrel, outerrel, innerrel, + clause_list = select_mergejoin_clauses(root, + joinrel, outerrel, innerrel, restrictlist, jointype, &mergejoin_allowed); - if (innerrel_keeps_unique(root, innerrel, outerrel, clause_list, true /* reverse */)) + /* + * XXX Seems a bit weird that it's called innerrel_keeps_unique but we + * seem to use it in both directions. Or what's the "reverse" for? The + * "reverse" name is not particularly descriptive. + */ + if (innerrel_keeps_unique(root, innerrel, outerrel, clause_list, true)) { - bool outer_impact = jointype == JOIN_FULL; + bool outer_impact = (jointype == JOIN_FULL); + + /* Inspect unique keys on the outer relation. */ foreach(lc, outerrel_ukey_ctx) { UniqueKeyContext ctx = (UniqueKeyContext)lfirst(lc); + /* + * If the output of the join does not include all the parts of the + * unique key, it's useless, so mark it accordingly and ignore it. + */ if (!list_is_subset(ctx->uniquekey->exprs, joinrel->reltarget->exprs)) { ctx->useful = false; continue; } - /* Outer relation has one row, and the unique key is not duplicated after join, - * the joinrel will still has one row unless the jointype == JOIN_FULL. + /* + * When the outer relation has one row, and the unique key is not + * duplicated after join, so the joinrel will still have just one + * row unless the jointype == JOIN_FULL. In that case we're done, + * it's the strictest unique key possible. + * + * If it's one-row with a JOIN_FULL, it might produce multiple + * rows with NULLs, so set multi_nullvals. We also need to set + * the exprs correctly since it can't be NIL any more. + * + * For other cases (not one-row relation), we just reuse the + * unique key, but we may need to tweak the multi_nullvals. */ if (outer_onerow && !outer_impact) { add_uniquekey_for_onerow(joinrel); return; } - else if (outer_onerow) + else if (outer_onerow) /* one-row and FULL join */ { - /* - * The onerow outerrel becomes multi rows and multi_nullvals - * will be changed to true. We also need to set the exprs correctly since it - * can't be NIL any more. - */ ListCell *lc2; + foreach(lc2, get_exprs_from_uniquekey(root, joinrel, outerrel, NULL)) { joinrel->uniquekeys = lappend(joinrel->uniquekeys, @@ -485,18 +684,38 @@ populate_joinrel_uniquekeys(PlannerInfo *root, RelOptInfo *joinrel, joinrel->uniquekeys = lappend(joinrel->uniquekeys, ctx->uniquekey); } + + /* + * Mark the unique key as added, so that we can ignore it later + * when combining unique keys from both sides of the join. + */ ctx->added_to_joinrel = true; } } + /* + * XXX Seems this actually checks if "outerrel keeps unique" so the name + * is misleading. Of maybe it's the previous block, not sure. + * + * XXX So why does this consider JOIN_FULL and JOIN_LEFT, while the previous + * block only cares about JOIN_FULL? + * + * XXX This is almost exact copy of the previous block, so maybe make it + * a separate function and just call it twice? + */ if (innerrel_keeps_unique(root, outerrel, innerrel, clause_list, false)) { - bool outer_impact = jointype == JOIN_FULL || jointype == JOIN_LEFT;; + bool outer_impact = (jointype == JOIN_FULL || jointype == JOIN_LEFT); + /* Inspect unique keys on the inner relation. */ foreach(lc, innerrel_ukey_ctx) { UniqueKeyContext ctx = (UniqueKeyContext)lfirst(lc); + /* + * If the output of the join does not include all the parts of the + * unique key, it's useless, so mark it accordingly and ignore it. + */ if (!list_is_subset(ctx->uniquekey->exprs, joinrel->reltarget->exprs)) { ctx->useful = false; @@ -529,29 +748,52 @@ populate_joinrel_uniquekeys(PlannerInfo *root, RelOptInfo *joinrel, ctx->uniquekey); } + + /* + * Mark the unique key as added, so that we can ignore it later + * when combining unique keys from both sides of the join. + */ ctx->added_to_joinrel = true; } } /* - * The combination of the UniqueKey from both sides is unique as well regardless - * of join type, but no bother to add it if its subset has been added to joinrel - * already or it is not useful for the joinrel. + * XXX What if either of the previous two conditions did not match? In + * that case we haven't updated the useful flag, and maybe the unique + * key is not useful, but we don't know, right? So we should not be + * using it in the next loop. Or maybe we should evaluate the flag + * before the loops. + */ + + /* + * The combination of the UniqueKey from both sides is unique as well, + * regardless of the join type. But don't bother to add it if its + * subset has been added to joinrel already or when it's not useful for + * the joinrel. + * + * XXX Maybe we should have a flag that both sides have useful keys? + * Or maybe the loops are short/cheap? */ foreach(lc, outerrel_ukey_ctx) { UniqueKeyContext ctx1 = (UniqueKeyContext) lfirst(lc); + + /* when not useful or already added to the joinrel, skip it */ if (ctx1->added_to_joinrel || !ctx1->useful) continue; + foreach(lc2, innerrel_ukey_ctx) { UniqueKeyContext ctx2 = (UniqueKeyContext) lfirst(lc2); + + /* when not useful or already added to the joinrel, skip it */ if (ctx2->added_to_joinrel || !ctx2->useful) continue; + + /* If we add a onerow UniqueKey, we don't need another key. */ if (add_combined_uniquekey(root, joinrel, outerrel, innerrel, ctx1->uniquekey, ctx2->uniquekey, jointype)) - /* If we set a onerow UniqueKey to joinrel, we don't need other. */ return; } } @@ -560,8 +802,9 @@ populate_joinrel_uniquekeys(PlannerInfo *root, RelOptInfo *joinrel, /* * convert_subquery_uniquekeys + * Covert the UniqueKey in subquery to outer relation. * - * Covert the UniqueKey in subquery to outer relation. + * XXX Explain what exactly does the conversion do? */ void convert_subquery_uniquekeys(PlannerInfo *root, RelOptInfo *currel, @@ -618,12 +861,14 @@ void convert_subquery_uniquekeys(PlannerInfo *root, /* * innerrel_keeps_unique + * Check if Unique key on the innerrel is valid after join. * - * Check if Unique key of the innerrel is valid after join. innerrel's UniqueKey - * will be still valid if innerrel's any-column mergeop outrerel's uniquekey - * exists in clause_list. + * innerrel's UniqueKey will be still valid if innerrel's any-column mergeop + * outrerel's uniquekey exists in clause_list * * Note: the clause_list must be a list of mergeable restrictinfo already. + * + * XXX Misleading name? We seem to use it for "outerrel_keeps_unique" too. */ static bool innerrel_keeps_unique(PlannerInfo *root, @@ -634,26 +879,32 @@ innerrel_keeps_unique(PlannerInfo *root, { ListCell *lc, *lc2, *lc3; + /* XXX probably not needed, duplicate with the check in the caller + * (populate_joinrel_uniquekeys). But it's cheap. */ if (outerrel->uniquekeys == NIL || innerrel->uniquekeys == NIL) return false; /* Check if there is outerrel's uniquekey in mergeable clause. */ foreach(lc, outerrel->uniquekeys) { - List *outer_uq_exprs = lfirst_node(UniqueKey, lc)->exprs; - bool clauselist_matchs_all_exprs = true; + List *outer_uq_exprs = lfirst_node(UniqueKey, lc)->exprs; + bool clauselist_matchs_all_exprs = true; + foreach(lc2, outer_uq_exprs) { Node *outer_uq_expr = lfirst(lc2); bool find_uq_expr_in_clauselist = false; + foreach(lc3, clause_list) { RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc3); Node *outer_expr; + if (reverse) outer_expr = rinfo->outer_is_left ? get_rightop(rinfo->clause) : get_leftop(rinfo->clause); else outer_expr = rinfo->outer_is_left ? get_leftop(rinfo->clause) : get_rightop(rinfo->clause); + if (equal(outer_expr, outer_uq_expr)) { find_uq_expr_in_clauselist = true; @@ -677,22 +928,37 @@ innerrel_keeps_unique(PlannerInfo *root, /* * relation_is_onerow - * Check if it is a one-row relation by checking UniqueKey. + * Check if it is a one-row relation by checking UniqueKey. + * + * The one-row is a special case - there has to be just a single unique key, + * with no expressions. */ bool relation_is_onerow(RelOptInfo *rel) { UniqueKey *ukey; - if (rel->uniquekeys == NIL) + + /* there has to be exactly one unique key */ + if (list_length(rel->uniquekeys) != 1) return false; + ukey = linitial_node(UniqueKey, rel->uniquekeys); - return ukey->exprs == NIL && list_length(rel->uniquekeys) == 1; + + /* the unique key must have no expressions */ + return (ukey->exprs == NIL); } /* * relation_has_uniquekeys_for - * Returns true if we have proofs that 'rel' cannot return multiple rows with - * the same values in each of 'exprs'. Otherwise returns false. + * Determines if the relation has unique key for a list of expressions. + * + * Returns true iff we can prove that the relation cannot return multiple rows + * with the same values in the provided expression. + * + * allow_multinulls determines whether we allow multiple NULL values or not. + * + * The special "one-row" unique key is considered incompatible with all + * possible expressions. */ bool relation_has_uniquekeys_for(PlannerInfo *root, RelOptInfo *rel, @@ -710,20 +976,39 @@ relation_has_uniquekeys_for(PlannerInfo *root, RelOptInfo *rel, foreach(lc, rel->uniquekeys) { UniqueKey *ukey = lfirst_node(UniqueKey, lc); + if (ukey->multi_nullvals && !allow_multinulls) continue; + if (list_is_subset(ukey->exprs, exprs)) return true; } + return false; } /* * get_exprs_from_uniqueindex + * Return a list of expressions from a unique index. + * + * Provided with a list of expressions and opclass families, we try to match + * it to the index. If useful, we produce a list of index expressions (subset + * of the list we provided). + * + * We simply walk through the index expressions, and for each expression we + * check three things: * - * Return a list of exprs which is unique. set useful to false if this - * unique index is not useful for us. + * 1) If there's a matching (expr = Const) clause, we can simply ignore the + * expressions. Unique index on (a,b,c) guarantees uniqueness on (a,b) when + * there's condition (c=1). + * + * 2) Check that the index expression is present in the relation we're + * dealing with. If not, the unique key would be useless anyway, and the + * index can't produce unique key. + * + * XXX Shouldn't it be enough to return NULL when the index is not useful? + * The extra flag seems a bit unnecessary. */ static List * get_exprs_from_uniqueindex(IndexOptInfo *unique_index, @@ -743,18 +1028,19 @@ get_exprs_from_uniqueindex(IndexOptInfo *unique_index, indexpr_item = list_head(unique_index->indexprs); for(c = 0; c < unique_index->ncolumns; c++) { - int attr = unique_index->indexkeys[c]; - Expr *expr; - bool matched_const = false; - ListCell *lc1, *lc2; + int attr = unique_index->indexkeys[c]; + Expr *expr; + bool matched_const = false; + ListCell *lc1, *lc2; - if(attr > 0) + if (attr > 0) { + /* regular attribute, just use the expression from index tlist */ expr = list_nth_node(TargetEntry, unique_index->indextlist, c)->expr; } else if (attr == 0) { - /* Expression index */ + /* expression from the index */ expr = lfirst(indexpr_item); indexpr_item = lnext(unique_index->indexprs, indexpr_item); } @@ -764,29 +1050,43 @@ get_exprs_from_uniqueindex(IndexOptInfo *unique_index, Assert(false); } + /* should have a valid expression now */ + Assert(expr); + /* - * Check index_col = Const case with regarding to opfamily checking - * If we can remove the index_col from the final UniqueKey->exprs. + * Check if there's (index_col = Const) condition, and that it's using + * a compatible opfamily. If yes, we can remove the index_col from the + * final UniqueKey->exprs, because the value is constant (so removing + * it can't introduce duplicities). */ forboth(lc1, const_exprs, lc2, const_expr_opfamilies) { - if (list_member_oid((List *)lfirst(lc2), unique_index->opfamily[c]) - && match_index_to_operand((Node *) lfirst(lc1), c, unique_index)) + List *opfamilies = (List *) lfirst(lc2); + Node *cexpr = (Node *) lfirst(lc1); + + if (list_member_oid(opfamilies, unique_index->opfamily[c]) && + match_index_to_operand(cexpr, c, unique_index)) { matched_const = true; break; } } + /* it's constant, so ignore the expression */ if (matched_const) continue; - /* Check if the indexed expr is used in rel */ + /* + * Check if the indexed expr is used in rel. We do this after the + * (col = Const) check, because nn expression may be in a a restrict + * clause and not in the reltarget. So we don't want to rule out an + * index unnecessarily. + */ if (attr > 0) { /* - * Normal Indexed column, if the col is not used, then the index is useless - * for uniquekey. + * Normal indexed column, if the col is not used, then the index + * is useless for uniquekey. */ attr -= FirstLowInvalidHeapAttributeNumber; @@ -806,67 +1106,85 @@ get_exprs_from_uniqueindex(IndexOptInfo *unique_index, /* check not null property. */ if (attr == 0) { - /* We never know if a expression yields null or not */ + /* We never know if an expression yields null or not */ *multi_nullvals = true; } - else if (!bms_is_member(attr, unique_index->rel->notnullattrs) - && !bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, - unique_index->rel->notnullattrs)) + else if (!bms_is_member(attr, unique_index->rel->notnullattrs) && + !bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, + unique_index->rel->notnullattrs)) { *multi_nullvals = true; } exprs = lappend(exprs, expr); } + return exprs; } /* * add_uniquekey_for_onerow - * If we are sure that the relation only returns one row, then all the columns - * are unique. However we don't need to create UniqueKey for every column, we - * just set exprs = NIL and overwrites all the other UniqueKey on this RelOptInfo - * since this one has strongest semantics. + * Create a special unique key signifying that the rel has one row. + * + * If we are sure that the relation only returns one row (it might return + * no rows, but we still consider that unique), then all the columns are + * trivially unique. + * + * However we don't need to create UniqueKey with every column, we just + * set exprs = NIL, because that's easier to identify. We don't want to + * add unnecessary unique keys (such that we already have a unique key + * for a subset of the expressions), and with (exprs == NIL) we can just + * assume we have one unique key for each column in the rel. + * + * We discard all other unique keys, since it has the strongest semantics. */ void add_uniquekey_for_onerow(RelOptInfo *rel) { /* - * We overwrite the previous UniqueKey on purpose since this one has the - * strongest semantic. + * We overwrite the previous UniqueKey on purpose since this one has + * the strongest semantic (all other unique keys are implied by it). */ rel->uniquekeys = list_make1(makeUniqueKey(NIL, false)); } /* - * initililze_uniquecontext_for_joinrel - * Return a List of UniqueKeyContext for an inputrel + * initialize_uniquecontext_for_joinrel + * Return a List of UniqueKeyContext for an inputrel. */ static List * -initililze_uniquecontext_for_joinrel(RelOptInfo *inputrel) +initialize_uniquecontext_for_joinrel(RelOptInfo *inputrel) { - List *res = NIL; - ListCell *lc; - foreach(lc, inputrel->uniquekeys) + List *res = NIL; + ListCell *lc; + + foreach(lc, inputrel->uniquekeys) { UniqueKeyContext context; + context = palloc(sizeof(struct UniqueKeyContextData)); context->uniquekey = lfirst_node(UniqueKey, lc); context->added_to_joinrel = false; context->useful = true; + res = lappend(res, context); } + return res; } - /* * get_exprs_from_uniquekey - * Unify the way of get List of exprs from a one-row UniqueKey or - * normal UniqueKey. for the onerow case, every expr in rel1 is a valid - * UniqueKey. Return a List of exprs. + * Extract expressions that are part of a unique key. + * + * The meaning of the result is a bit different in regular and one-row cases. + * For the regular case, the list of expressions form a single unique key, + * i.e. the combination of values is unique. + * + * For the one-row case, each individual expression is known to be unique + * (simply because in a single row everything is unique). * * rel1: The relation which you want to get the exprs. * ukey: The UniqueKey you want to get the exprs. @@ -875,27 +1193,29 @@ static List * get_exprs_from_uniquekey(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *rel1, UniqueKey *ukey) { - ListCell *lc; - bool onerow = rel1 != NULL && relation_is_onerow(rel1); + ListCell *lc; + List *res = NIL; + bool onerow = (rel1 != NULL) && relation_is_onerow(rel1); - List *res = NIL; + /* We require at least one of those to be true. */ Assert(onerow || ukey); - if (onerow) - { - /* Only cares about the exprs still exist in joinrel */ - foreach(lc, joinrel->reltarget->exprs) - { - Bitmapset *relids = pull_varnos(root, lfirst(lc)); - if (bms_is_subset(relids, rel1->relids)) - { - res = lappend(res, list_make1(lfirst(lc))); - } - } - } - else + + /* if not a one-row unique key, just return the key's expressions */ + if (!onerow) + return list_make1(ukey->exprs); + + /* + * If it's a one-row relation, we simply extract the expressions that + * still exist in the reltarget. + */ + foreach(lc, joinrel->reltarget->exprs) { - res = list_make1(ukey->exprs); + Bitmapset *relids = pull_varnos(root, lfirst(lc)); + + if (bms_is_subset(relids, rel1->relids)) + res = lappend(res, list_make1(lfirst(lc))); } + return res; } @@ -910,55 +1230,67 @@ get_exprs_from_uniquekey(PlannerInfo *root, RelOptInfo *joinrel, /* * index_constains_partkey - * return true if the index contains the partiton key. + * Determines if the index includes a partition key. + * + * XXX Surely we already have a code doing this already? E.g. when creating + * a unique index on a partitioned table we define that. */ static bool -index_constains_partkey(RelOptInfo *partrel, IndexOptInfo *ind) +index_constains_partkey(RelOptInfo *partrel, IndexOptInfo *ind) { ListCell *lc; int i; + Assert(IS_PARTITIONED_REL(partrel)); Assert(partrel->part_scheme->partnatts > 0); for(i = 0; i < partrel->part_scheme->partnatts; i++) { - Node *part_expr = linitial(partrel->partexprs[i]); - bool found_in_index = false; + Node *part_expr = linitial(partrel->partexprs[i]); + bool found_in_index = false; + foreach(lc, ind->indextlist) { - Expr *index_expr = lfirst_node(TargetEntry, lc)->expr; + Expr *index_expr = lfirst_node(TargetEntry, lc)->expr; + if (equal(index_expr, part_expr)) { found_in_index = true; break; } } + if (!found_in_index) return false; } + return true; } /* * simple_indexinfo_equal + * Compare two indexes to determine if they are the same. + * + * We need to do this because simple_copy_indexinfo_to_parent does change + * some elements. So this is not exactly the same as calling equal(). * - * Used to check if the 2 index is same as each other. The index here - * is COPIED from childrel and did some tiny changes(see - * simple_copy_indexinfo_to_parent) + * XXX I wonder if we could simply use equal(), somehow? In fact, we should + * probably build something much simpler than IndexOptInfo, just enough to + * do the checks. */ static bool simple_indexinfo_equal(IndexOptInfo *ind1, IndexOptInfo *ind2) { Size oid_cmp_len = sizeof(Oid) * ind1->ncolumns; - return ind1->ncolumns == ind2->ncolumns && - ind1->unique == ind2->unique && - memcmp(ind1->indexkeys, ind2->indexkeys, sizeof(int) * ind1->ncolumns) == 0 && - memcmp(ind1->opfamily, ind2->opfamily, oid_cmp_len) == 0 && - memcmp(ind1->opcintype, ind2->opcintype, oid_cmp_len) == 0 && - memcmp(ind1->sortopfamily, ind2->sortopfamily, oid_cmp_len) == 0 && - equal(get_tlist_exprs(ind1->indextlist, true), - get_tlist_exprs(ind2->indextlist, true)); + return ((ind1->ncolumns == ind2->ncolumns) && + (ind1->unique == ind2->unique) && + (memcmp(ind1->indexkeys, ind2->indexkeys, sizeof(int) * ind1->ncolumns) == 0) && + (memcmp(ind1->opfamily, ind2->opfamily, oid_cmp_len) == 0) && + (memcmp(ind1->opcintype, ind2->opcintype, oid_cmp_len) == 0) && + (memcmp(ind1->sortopfamily, ind2->sortopfamily, oid_cmp_len) == 0) && + (equal(get_tlist_exprs(ind1->indextlist, true), + get_tlist_exprs(ind2->indextlist, true)))); } @@ -981,11 +1313,21 @@ simple_indexinfo_equal(IndexOptInfo *ind1, IndexOptInfo *ind2) /* - * simple_copy_indexinfo_to_parent (from partition) - * Copy the IndexInfo from child relation to parent relation with some modification, - * which is used to test: - * 1. If the same index exists in all the childrels. + * simple_copy_indexinfo_to_parent + * Copy index info from child to parent, with necessary tweaks. + * + * We use this copy to check: + * + * 1. If the same/matching index exists in all the childrels. * 2. If the parentrel->reltarget/basicrestrict info matches this index. + * + * XXX IMHO we should probably build something much simpler than a full + * IndexOptInfo copy, just enough to do the checks. + * + * XXX The fact that we copy so much data seems wrong, and having to + * define macros from copyfuncs.c seems like a very suspicious thing. + * One reason is that IndeOptInfo is fairly large struct, especially + * with all the fields, and we allocate it very often. */ static IndexOptInfo * simple_copy_indexinfo_to_parent(PlannerInfo *root, @@ -1027,20 +1369,24 @@ simple_copy_indexinfo_to_parent(PlannerInfo *root, /* * adjust_partition_unique_indexlist + * Checks and eliminates indexes that do not exist on the child relation. * - * global_unique_indexes: At the beginning, it contains the copy & modified - * unique index from the first partition. And then check if each index in it still - * exists in the following partitions. If no, remove it. at last, it has an - * index list which exists in all the partitions. + * Walks the list of unique indexes, and eliminates those that don't match + * the child relation (i.e. where a matching child index does not exist). + * This is used to iteratively filter the list of candidate unique keys. + * + * After processing all child relations, the list contains only indexes that + * exist in all the child relations. */ static void adjust_partition_unique_indexlist(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, - List **global_unique_indexes) + List **indexes) { ListCell *lc, *lc2; - foreach(lc, *global_unique_indexes) + + foreach(lc, *indexes) { IndexOptInfo *g_ind = lfirst_node(IndexOptInfo, lc); bool found_in_child = false; @@ -1049,23 +1395,45 @@ adjust_partition_unique_indexlist(PlannerInfo *root, { IndexOptInfo *p_ind = lfirst_node(IndexOptInfo, lc2); IndexOptInfo *p_ind_copy; - if (!p_ind->unique || !p_ind->immediate || - (p_ind->indpred != NIL && !p_ind->predOK)) + + /* + * Ignore child indexes that can't possibly match (not unique or + * immediate, etc.) + * + * XXX We do these checks in many places, so maybe turn it into + * a reusable macro? + */ + if ((!p_ind->unique) || (!p_ind->immediate) || + (p_ind->indpred != NIL) && (!p_ind->predOK)) continue; + + /* + * XXX This seems possibly quite expensive. Imagine there are many + * child relations, with a bunch of unique indexes each. Then this + * generates a copy for each unique index in each child relation, + * something like O(N^2/2) copies. + */ p_ind_copy = simple_copy_indexinfo_to_parent(root, parentrel, p_ind); + + /* Found a matching index for the child relation, we're done. */ if (simple_indexinfo_equal(p_ind_copy, g_ind)) { found_in_child = true; break; } } + + /* No matching index in the child, so remove it from the list. */ if (!found_in_child) - /* The index doesn't exist in childrel, remove it from global_unique_indexes */ - *global_unique_indexes = foreach_delete_current(*global_unique_indexes, lc); + *indexes = foreach_delete_current(*indexes, lc); } } -/* Helper function for groupres/distinctrel */ +/* + * Helper function for groupres/distinctrel + * + * FIXME Not sure about this. + */ static void add_uniquekey_from_sortgroups(PlannerInfo *root, RelOptInfo *rel, List *sortgroups) { @@ -1073,27 +1441,32 @@ add_uniquekey_from_sortgroups(PlannerInfo *root, RelOptInfo *rel, List *sortgrou List *exprs; /* - * XXX: If there are some vars which is not in current levelsup, the semantic is - * imprecise, should we avoid it or not? levelsup = 1 is just a demo, maybe we need to - * check every level other than 0, if so, looks we have to write another - * pull_var_walker. + * XXX: If there are some vars which are not in the current levelsup, the + * semantic is imprecise, should we avoid it or not? levelsup = 1 is just + * a demo, maybe we need to check every level other than 0, if so, looks + * we have to write another pull_var_walker. */ List *upper_vars = pull_vars_of_level((Node*)sortgroups, 1); if (upper_vars != NIL) return; + /* sortgroupclause can't be multi_nullvals */ exprs = get_sortgrouplist_exprs(sortgroups, parse->targetList); rel->uniquekeys = lappend(rel->uniquekeys, - makeUniqueKey(exprs, - false /* sortgroupclause can't be multi_nullvals */)); + makeUniqueKey(exprs, false)); } /* * add_combined_uniquekey - * The combination of both UniqueKeys is a valid UniqueKey for joinrel no matter - * the jointype. + * Add a unique key for a join, combined from keys on inner/outer side. + * + * The combination of both UniqueKeys is a valid UniqueKey for joinrel no + * matter what's the exact jointype. + * + * Returns true if the unique key is "one-row" variant, so that the caller + * can stop considering further combinations. */ bool add_combined_uniquekey(PlannerInfo *root, @@ -1104,32 +1477,47 @@ add_combined_uniquekey(PlannerInfo *root, UniqueKey *inner_ukey, JoinType jointype) { + bool multi_nullvals; + ListCell *lc1, *lc2; - ListCell *lc1, *lc2; - - /* Either side has multi_nullvals or we have outer join, - * the combined UniqueKey has multi_nullvals */ - bool multi_nullvals = outer_ukey->multi_nullvals || + /* + * If either side has multi_nullvals, or we are dealing with an outer join, + * the combined UniqueKey has multi_nullvals too. + */ + multi_nullvals = outer_ukey->multi_nullvals || inner_ukey->multi_nullvals || IS_OUTER_JOIN(jointype); /* The only case we can get onerow joinrel after join */ - if (relation_is_onerow(outer_rel) - && relation_is_onerow(inner_rel) - && jointype == JOIN_INNER) + if (relation_is_onerow(outer_rel) && + relation_is_onerow(inner_rel) && + jointype == JOIN_INNER) { add_uniquekey_for_onerow(joinrel); return true; } + /* + * XXX Isn't this wrong? Why is it combining expressions that are part + * of the two unique keys? Imagine we have outer unique key on (a1, a2) + * and inner outer key on (b1, b2). Then this adds four unique keys + * for the join (a1,b1), (a1,b2), (a2,b1) and (a2,b2). Shouldn't it + * just add (a1,a2,b1,b2)? + */ foreach(lc1, get_exprs_from_uniquekey(root, joinrel, outer_rel, outer_ukey)) { + /* + * XXX This calls get_exprs_from_uniquekey repeatedly for each outer + * loop. Maybe we should calculate it just once before the loop. + */ foreach(lc2, get_exprs_from_uniquekey(root, joinrel, inner_rel, inner_ukey)) { List *exprs = list_concat_copy(lfirst_node(List, lc1), lfirst_node(List, lc2)); + joinrel->uniquekeys = lappend(joinrel->uniquekeys, makeUniqueKey(exprs, multi_nullvals)); } } + return false; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 8d8e493f5c..f29b65c07b 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -2387,6 +2387,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, add_path(final_rel, path); } + /* XXX comment? can we simply just copy the unique keys to the final relation? */ simple_copy_uniquekeys(current_rel, final_rel); /* @@ -3902,7 +3903,9 @@ create_grouping_paths(PlannerInfo *root, set_cheapest(grouped_rel); + /* XXX does this apply to grouping sets too? */ populate_grouprel_uniquekeys(root, grouped_rel, input_rel); + return grouped_rel; } @@ -4625,7 +4628,10 @@ create_window_paths(PlannerInfo *root, /* Now choose the best path(s) */ set_cheapest(window_rel); + + /* XXX comment? */ simple_copy_uniquekeys(input_rel, window_rel); + return window_rel; } @@ -4939,7 +4945,10 @@ create_distinct_paths(PlannerInfo *root, /* Now choose the best path(s) */ set_cheapest(distinct_rel); + + /* XXX comment */ populate_distinctrel_uniquekeys(root, input_rel, distinct_rel); + return distinct_rel; } @@ -5200,6 +5209,7 @@ create_ordered_paths(PlannerInfo *root, */ Assert(ordered_rel->pathlist != NIL); + /* XXX comment */ simple_copy_uniquekeys(input_rel, ordered_rel); return ordered_rel; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index b7626545bf..72a3f3c598 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -691,6 +691,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root, /* Add the UniqueKeys */ populate_unionrel_uniquekeys(root, result_rel); + return result_rel; } diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 3eec1f4d74..c9829c5fc4 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -755,6 +755,7 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, pseudoconstant, rinfo->security_level, NULL, NULL, NULL); + /* XXX This is a bit weird, doing this outside make_restrictinfo */ child_rinfo->mergeopfamilies = rinfo->mergeopfamilies; childquals = lappend(childquals, child_rinfo); /* track minimum security level among child quals */ -- 2.30.2 --------------F495F6B18672582269F00FF9 Content-Type: text/x-patch; charset=UTF-8; name="0005-Extend-UniqueKeys-20210317.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0005-Extend-UniqueKeys-20210317.patch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 06/10] review @ 2021-03-17 01:19 Tomas Vondra <tomas.vondra@postgresql.org> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-03-17 01:19 UTC (permalink / raw) --- src/backend/optimizer/path/uniquekeys.c | 2 ++ src/backend/optimizer/plan/planner.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/backend/optimizer/path/uniquekeys.c b/src/backend/optimizer/path/uniquekeys.c index b9567ca5c8..0e6c826fcd 100644 --- a/src/backend/optimizer/path/uniquekeys.c +++ b/src/backend/optimizer/path/uniquekeys.c @@ -1522,6 +1522,7 @@ add_combined_uniquekey(PlannerInfo *root, return false; } +/* FIXME comment */ List* build_uniquekeys(PlannerInfo *root, List *sortclauses) { @@ -1548,6 +1549,7 @@ build_uniquekeys(PlannerInfo *root, List *sortclauses) return result; } +/* FIXME comment */ bool query_has_uniquekeys_for(PlannerInfo *root, List *pathuniquekeys, bool allow_multinulls) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 62c0e46c49..f66b9d4898 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -4857,11 +4857,13 @@ create_distinct_paths(PlannerInfo *root, Path *path = (Path *) lfirst(lc); if (pathkeys_contained_in(needed_pathkeys, path->pathkeys)) + { add_path(distinct_rel, (Path *) create_upper_unique_path(root, distinct_rel, path, list_length(root->distinct_pathkeys), numDistinctRows)); + } } foreach(lc, input_rel->unique_pathlist) -- 2.30.2 --------------F495F6B18672582269F00FF9 Content-Type: text/x-patch; charset=UTF-8; name="0007-Index-skip-scan-20210317.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0007-Index-skip-scan-20210317.patch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 08/10] review @ 2021-03-17 01:36 Tomas Vondra <tomas.vondra@postgresql.org> 0 siblings, 0 replies; 190+ messages in thread From: Tomas Vondra @ 2021-03-17 01:36 UTC (permalink / raw) --- src/backend/commands/explain.c | 1 + src/backend/executor/nodeIndexscan.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a160de5493..3b0b38099f 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1110,6 +1110,7 @@ ExplainIndexSkipScanKeys(int skipPrefixSize, ExplainState *es) { if (skipPrefixSize > 0) { + /* FIXME Why not to show this for TEXT output? */ if (es->format != EXPLAIN_FORMAT_TEXT) ExplainPropertyInteger("Distinct Prefix", NULL, skipPrefixSize, es); } diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 71aac4493d..720442b731 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -124,6 +124,8 @@ IndexNext(IndexScanState *node) node->iss_ScanDesc = scandesc; + /* FIXME Is the else branch necessary? */ + /* Index skip scan assumes xs_want_itup, so set it to true */ if (indexscan->indexskipprefixsize > 0) node->iss_ScanDesc->xs_want_itup = true; -- 2.30.2 --------------F495F6B18672582269F00FF9 Content-Type: text/x-patch; charset=UTF-8; name="0009-Btree-implementation-of-skipping-20210317.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0009-Btree-implementation-of-skipping-20210317.patch" ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH 2/2] review @ 2025-04-03 18:14 Álvaro Herrera <alvherre@alvh.no-ip.org> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2025-04-03 18:14 UTC (permalink / raw) --- contrib/pgcrypto/crypt-gensalt.c | 26 ++-- contrib/pgcrypto/crypt-sha.c | 248 ++++++++++++------------------- contrib/pgcrypto/px-crypt.c | 14 +- contrib/pgcrypto/px-crypt.h | 6 +- 4 files changed, 121 insertions(+), 173 deletions(-) diff --git a/contrib/pgcrypto/crypt-gensalt.c b/contrib/pgcrypto/crypt-gensalt.c index 922f9eea6fa..ef68ae0e49e 100644 --- a/contrib/pgcrypto/crypt-gensalt.c +++ b/contrib/pgcrypto/crypt-gensalt.c @@ -186,6 +186,9 @@ _crypt_gensalt_blowfish_rn(unsigned long count, return output; } +/* + * Helper for _crypt_gensalt_sha256_rn and _crypt_gensalt_sha512_rn + */ static char * _crypt_gensalt_sha(unsigned long count, const char *input, int size, char *output, int output_size) @@ -196,32 +199,25 @@ _crypt_gensalt_sha(unsigned long count, /* output buffer must be allocated with PX_MAX_SALT_LEN bytes */ if (PX_MAX_SALT_LEN < result_bufsize) - { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid size of salt"))); - } + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid size of salt")); /* * Care must be taken to not exceed the buffer size allocated for the * input character buffer. */ - if ((PX_SHACRYPT_SALT_MAX_LEN != size) - || (output_size < size)) - { + if ((PX_SHACRYPT_SALT_MAX_LEN != size) || (output_size < size)) ereport(ERROR, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("invalid length of salt buffer"))); - } + errcode(ERRCODE_INTERNAL_ERROR), + errmsg("invalid length of salt buffer")); /* Skip magic bytes, set by callers */ s_ptr += 3; if ((rc = pg_snprintf(s_ptr, 18, "rounds=%ld$", count)) <= 0) - { ereport(ERROR, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("cannot format salt string"))); - } + errcode(ERRCODE_INTERNAL_ERROR), + errmsg("cannot format salt string")); /* s_ptr should now be positioned at the start of the salt string */ s_ptr += rc; @@ -242,6 +238,7 @@ _crypt_gensalt_sha(unsigned long count, return output; } +/* gen_list->gen function for sha512 */ char * _crypt_gensalt_sha512_rn(unsigned long count, char const *input, int size, @@ -256,6 +253,7 @@ _crypt_gensalt_sha512_rn(unsigned long count, return _crypt_gensalt_sha(count, input, size, output, output_size); } +/* gen_list->gen function for sha256 */ char * _crypt_gensalt_sha256_rn(unsigned long count, const char *input, int size, diff --git a/contrib/pgcrypto/crypt-sha.c b/contrib/pgcrypto/crypt-sha.c index 13ee8913052..862f3fb9a68 100644 --- a/contrib/pgcrypto/crypt-sha.c +++ b/contrib/pgcrypto/crypt-sha.c @@ -44,6 +44,8 @@ * */ #include "postgres.h" + +#include "common/string.h" #include "miscadmin.h" #include "px-crypt.h" @@ -78,23 +80,20 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle const char *dec_salt_binary; /* pointer into the real salt string */ StringInfo decoded_salt = NULL; /* decoded salt string */ - unsigned char sha_buf[PX_SHACRYPT_DIGEST_MAX_LEN]; - unsigned char sha_buf_tmp[PX_SHACRYPT_DIGEST_MAX_LEN]; /* temporary buffer for - * digests */ + /* temporary buffer for digests */ + unsigned char sha_buf_tmp[PX_SHACRYPT_DIGEST_MAX_LEN]; char rounds_custom = 0; char *p_bytes = NULL; char *s_bytes = NULL; char *cp = NULL; const char *fp = NULL; /* intermediate pointer within salt string */ const char *ep = NULL; /* holds pointer to the end of the salt string */ - size_t buf_size = 0; /* buffer size for sha256crypt/sha512crypt */ unsigned int block; /* number of bytes processed */ - unsigned long rounds = PX_SHACRYPT_ROUNDS_DEFAULT; - - unsigned len, + uint32 rounds = PX_SHACRYPT_ROUNDS_DEFAULT; + unsigned int len, salt_len = 0; /* Init result buffer */ @@ -106,22 +105,16 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle return NULL; if (pw == NULL) - { elog(ERROR, "null value for password rejected"); - } if (salt == NULL) - { elog(ERROR, "null value for salt rejected"); - } /* * Make sure result buffers are large enough. */ if (dstlen < PX_SHACRYPT_BUF_LEN) - { elog(ERROR, "insufficient result buffer size to encrypt password"); - } /* Init contents of buffers properly */ memset(&sha_buf, '\0', sizeof(sha_buf)); @@ -138,27 +131,22 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle * Analyze and prepare the salt string * * The magic string should be specified in the first three bytes of the - * salt string. But do some sanity checks before. + * salt string. Do some sanity checks first. */ if (strlen(dec_salt_binary) < 3) - { ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid salt"))); - } + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid salt")); /* * Check format of magic bytes. These should define either 5=sha256crypt * or 6=sha512crypt in the second byte, enclosed by ascii dollar signs. */ - if ((dec_salt_binary[0] != '$') - && (dec_salt_binary[2] != '$')) - { + if ((dec_salt_binary[0] != '$') || (dec_salt_binary[2] != '$')) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid format of salt")), + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid format of salt"), errhint("magic byte format for shacrypt is either \"$5$\" or \"$6$\"")); - } /* * Check magic byte for supported shacrypt digest. @@ -190,62 +178,54 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle * string, we don't treat this special: This is just absorbed as part of * the salt with up to PX_SHACRYPT_SALT_LEN_MAX. * - * Unknown magic byte is handled later below + * Unknown magic byte is handled further below. */ if (strncmp(dec_salt_binary, rounds_prefix, sizeof(rounds_prefix) - 1) == 0) { - const char *num = dec_salt_binary + sizeof(rounds_prefix) - 1; char *endp; - long srounds = strtoul(num, &endp, 10); + int srounds = strtoint(num, &endp, 10); - if (*endp == '$') - { - dec_salt_binary = endp + 1; - - /* - * We violate supported lower or upper bound of rounds, but in - * this case we change this value to the supported lower or upper - * value. We don't do this silently and print a NOTICE in such a - * case. - * - * Note that a salt string generated with gen_salt() would never - * generated such a salt string, since it would error out. - * - * But Drepper's upstream reference implementation supports this - * when passing the salt string directly, so we maintain - * compatibility here. - */ - if (srounds > PX_SHACRYPT_ROUNDS_MAX) - { - ereport(NOTICE, - errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("rounds=%ld exceeds maximum supported value (%ld), using %ld instead", - srounds, PX_SHACRYPT_ROUNDS_MAX, - PX_SHACRYPT_ROUNDS_MAX)); - srounds = PX_SHACRYPT_ROUNDS_MAX; - } - else if (srounds < PX_SHACRYPT_ROUNDS_MIN) - { - ereport(NOTICE, - errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("rounds=%ld is below supported value (%ld), using %ld instead", - srounds, PX_SHACRYPT_ROUNDS_MIN, - PX_SHACRYPT_ROUNDS_MIN)); - srounds = PX_SHACRYPT_ROUNDS_MIN; - } - - rounds = (unsigned long) srounds; - rounds_custom = 1; - } - else - { + if (*endp != '$') ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("could not parse salt options")); + + dec_salt_binary = endp + 1; + + /* + * We violate supported lower or upper bound of rounds, but in this + * case we change this value to the supported lower or upper value. We + * don't do this silently and print a NOTICE in such a case. + * + * Note that a salt string generated with gen_salt() would never + * generated such a salt string, since it would error out. + * + * But Drepper's upstream reference implementation supports this when + * passing the salt string directly, so we maintain compatibility. + */ + if (srounds > PX_SHACRYPT_ROUNDS_MAX) + { + ereport(NOTICE, + errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("rounds=%d exceeds maximum supported value (%d), using %d instead", + srounds, PX_SHACRYPT_ROUNDS_MAX, + PX_SHACRYPT_ROUNDS_MAX)); + srounds = PX_SHACRYPT_ROUNDS_MAX; + } + else if (srounds < PX_SHACRYPT_ROUNDS_MIN) + { + ereport(NOTICE, + errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("rounds=%d is below supported value (%d), using %d instead", + srounds, PX_SHACRYPT_ROUNDS_MIN, + PX_SHACRYPT_ROUNDS_MIN)); + srounds = PX_SHACRYPT_ROUNDS_MIN; } + rounds = (uint32) srounds; + rounds_custom = 1; } /* @@ -294,9 +274,7 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle } if (rounds_custom > 0) - { - appendStringInfo(out_buf, "rounds=%lu$", rounds); - } + appendStringInfo(out_buf, "rounds=%u$", rounds); /* * We need the real decoded salt string from salt input, this is every @@ -326,47 +304,34 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle * this point. */ fp = strstr(dec_salt_binary, magic_bytes[0]); - if (fp != NULL) - { elog(ERROR, "bogus magic byte found in salt string"); - } fp = strstr(dec_salt_binary, magic_bytes[1]); - if (fp != NULL) - { elog(ERROR, "bogus magic byte found in salt string"); - } /* * This looks very strict, but we assume the caller did something * wrong when we see a "rounds=" option here. */ fp = strstr(dec_salt_binary, rounds_prefix); - if (fp != NULL) - { elog(ERROR, "invalid rounds option specified in salt string"); - } if (*ep != '$') { if (isalpha(*ep) || isdigit(*ep) || (*ep == '.') || (*ep == '/')) - { appendStringInfoCharMacro(decoded_salt, *ep); - } else - { elog(ERROR, "invalid character in salt string: \"%c\"", *ep); - } } else { /* * We encountered a '$' marker. Check if we already absorbed some * bytes from input. If true, we are optimistic and terminate at - * this stage. Of not, we try further. + * this stage. If not, we try further. * * If we already consumed enough bytes for the salt string, * everything that is after this marker is considered to be part @@ -375,23 +340,19 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle if (decoded_salt->len > 0) break; } - } salt_len = decoded_salt->len; appendStringInfoString(out_buf, decoded_salt->data); - elog(DEBUG1, "using salt \"%s\", salt len = %d, rounds = %lu", + elog(DEBUG1, "using salt \"%s\", salt len = %d, rounds = %u", decoded_salt->data, decoded_salt->len, rounds); /* - * Sanity check: - * - * At this point the salt string buffer must not exceed expected size + * Sanity check: at this point the salt string buffer must not exceed + * expected size. */ if (out_buf->len > (3 + 17 * rounds_custom + salt_len)) - { elog(ERROR, "unexpected length of salt string"); - } /*- * 1. Start digest A @@ -413,29 +374,26 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle px_md_update(digestB, (const unsigned char *) pw, len); px_md_finish(digestB, sha_buf); - /*- - * 9. For each block of (excluding the NULL byte), add - * digest B to digest A. + /* + * 9. For each block (excluding the NULL byte), add digest B to digest A. */ for (block = len; block > buf_size; block -= buf_size) - { px_md_update(digestA, sha_buf, buf_size); - } /*- - * 10 For the remaining N bytes of the password string, add - * the first N bytes of digest B to A */ + * 10. For the remaining N bytes of the password string, add the first N + * bytes of digest B to A. + */ px_md_update(digestA, sha_buf, block); /*- - * 11 For each bit of the binary representation of the length of the - * password string up to and including the highest 1-digit, starting - * from to lowest bit position (numeric value 1) + * 11. For each bit of the binary representation of the length of the + * password string up to and including the highest 1-digit, starting from + * to lowest bit position (numeric value 1) * * a) for a 1-digit add digest B (sha_buf) to digest A * b) for a 0-digit add the password string */ - block = len; while (block) { @@ -447,10 +405,10 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle block >>= 1; } - /* 12 Finalize digest A */ + /* 12. Finalize digest A */ px_md_finish(digestA, sha_buf); - /* 13 Start digest DP */ + /* 13. Start digest DP */ px_md_reset(digestB); /*- @@ -458,20 +416,17 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle * to the digest DP */ for (block = len; block > 0; block--) - { px_md_update(digestB, (const unsigned char *) pw, len); - } - /* 15 Finalize digest DP */ + /* 15. Finalize digest DP */ px_md_finish(digestB, sha_buf_tmp); /*- - * 16 produce byte sequence P with same length as password. - * + * 16. produce byte sequence P with same length as password. * a) for each block of 32 or 64 bytes of length of the password * string the entire digest DP is used * b) for the remaining N (up to 31 or 63) bytes use the - * first N bytes of digest DP + * first N bytes of digest DP */ if ((p_bytes = palloc0(len)) == NULL) { @@ -484,30 +439,28 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle memcpy(cp, sha_buf_tmp, block); /* - * 17 Start digest DS + * 17. Start digest DS */ px_md_reset(digestB); /*- - * 18 Repeat the following 16+A[0] times, where A[0] represents the first + * 18. Repeat the following 16+A[0] times, where A[0] represents the first * byte in digest A interpreted as an 8-bit unsigned value * add the salt to digest DS */ for (block = 16 + sha_buf[0]; block > 0; block--) - { px_md_update(digestB, (const unsigned char *) dec_salt_binary, salt_len); - } /* - * 19 Finalize digest DS + * 19. Finalize digest DS */ px_md_finish(digestB, sha_buf_tmp); /*- - * 20 Produce byte sequence S of the same length as the salt string where + * 20. Produce byte sequence S of the same length as the salt string where * * a) for each block of 32 or 64 bytes of length of the salt string the - * entire digest DS is used + * entire digest DS is used * * b) for the remaining N (up to 31 or 63) bytes use the first N * bytes of digest DS @@ -516,27 +469,24 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle goto error; for (cp = s_bytes, block = salt_len; block > buf_size; block -= buf_size, cp += buf_size) - { memcpy(cp, sha_buf_tmp, buf_size); - } memcpy(cp, sha_buf_tmp, block); /* Make sure we don't leave something important behind */ px_memset(&sha_buf_tmp, 0, sizeof sha_buf); /*- - * 21 Repeat a loop according to the number specified in the rounds=<N> - * specification in the salt (or the default value if none is - * present). Each round is numbered, starting with 0 and up to N-1. + * 21. Repeat a loop according to the number specified in the rounds=<N> + * specification in the salt (or the default value if none is + * present). Each round is numbered, starting with 0 and up to N-1. * - * The loop uses a digest as input. In the first round it is the - * digest produced in step 12. In the latter steps it is the digest - * produced in step 21.h of the previous round. The following text - * uses the notation "digest A/B" to describe this behavior. + * The loop uses a digest as input. In the first round it is the + * digest produced in step 12. In the latter steps it is the digest + * produced in step 21.h of the previous round. The following text + * uses the notation "digest A/B" to describe this behavior. */ for (block = 0; block < rounds; block++) { - /* * Make it possible to abort in case large values for "rounds" are * specified. @@ -546,29 +496,25 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle /* a) start digest B */ px_md_reset(digestB); - /* - * b) for odd round numbers add the byte sequense P to digest B c) for - * even round numbers add digest A/B + /*- + * b) for odd round numbers add the byte sequence P to digest B + * c) for even round numbers add digest A/B */ px_md_update(digestB, (block & 1) ? (const unsigned char *) p_bytes : sha_buf, (block & 1) ? len : buf_size); /* d) for all round numbers not divisible by 3 add the byte sequence S */ - if (block % 3) - { + if ((block % 3) != 0) px_md_update(digestB, (const unsigned char *) s_bytes, salt_len); - } /* e) for all round numbers not divisible by 7 add the byte sequence P */ - if (block % 7) - { + if ((block % 7) != 0) px_md_update(digestB, (const unsigned char *) p_bytes, len); - } - /* - * f) for odd round numbers add digest A/C g) for even round numbers - * add the byte sequence P + /*- + * f) for odd round numbers add digest A/C + * g) for even round numbers add the byte sequence P */ px_md_update(digestB, (block & 1) ? sha_buf : (const unsigned char *) p_bytes, @@ -576,7 +522,6 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle /* h) finish digest C. */ px_md_finish(digestB, sha_buf); - } px_md_free(digestA); @@ -594,15 +539,15 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle /* prepare final result buffer */ appendStringInfoCharMacro(out_buf, '$'); -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ - int i = (N); \ - while (i-- > 0) \ - { \ +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int i = (N); \ + while (i-- > 0) \ + { \ appendStringInfoCharMacro(out_buf, _crypt_itoa64[w & 0x3f]); \ - w >>= 6; \ - } \ + w >>= 6; \ + } \ } while (0) switch (type) @@ -655,7 +600,6 @@ px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstle case PGCRYPTO_SHA_UNKOWN: /* we shouldn't land here ... */ elog(ERROR, "unsupported digest length"); - } *cp = '\0'; @@ -690,7 +634,7 @@ error: destroyStringInfo(out_buf); ereport(ERROR, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("cannot create encrypted password"))); + errcode(ERRCODE_INTERNAL_ERROR), + errmsg("cannot create encrypted password")); return NULL; /* keep compiler quiet */ } diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c index a786277aeac..d7729eec9bc 100644 --- a/contrib/pgcrypto/px-crypt.c +++ b/contrib/pgcrypto/px-crypt.c @@ -139,10 +139,16 @@ static struct generator gen_list[] = { {"md5", _crypt_gensalt_md5_rn, 6, 0, 0, 0}, {"xdes", _crypt_gensalt_extended_rn, 3, PX_XDES_ROUNDS, 1, 0xFFFFFF}, {"bf", _crypt_gensalt_blowfish_rn, 16, PX_BF_ROUNDS, 4, 31}, - {"sha256crypt", _crypt_gensalt_sha256_rn, PX_SHACRYPT_SALT_MAX_LEN, - PX_SHACRYPT_ROUNDS_DEFAULT, PX_SHACRYPT_ROUNDS_MIN, PX_SHACRYPT_ROUNDS_MAX}, - {"sha512crypt", _crypt_gensalt_sha512_rn, PX_SHACRYPT_SALT_MAX_LEN, - PX_SHACRYPT_ROUNDS_DEFAULT, PX_SHACRYPT_ROUNDS_MIN, PX_SHACRYPT_ROUNDS_MAX}, + { + "sha256crypt", _crypt_gensalt_sha256_rn, + PX_SHACRYPT_SALT_MAX_LEN, PX_SHACRYPT_ROUNDS_DEFAULT, + PX_SHACRYPT_ROUNDS_MIN, PX_SHACRYPT_ROUNDS_MAX + }, + { + "sha512crypt", _crypt_gensalt_sha512_rn, + PX_SHACRYPT_SALT_MAX_LEN, PX_SHACRYPT_ROUNDS_DEFAULT, + PX_SHACRYPT_ROUNDS_MIN, PX_SHACRYPT_ROUNDS_MAX + }, {NULL, NULL, 0, 0, 0, 0} }; diff --git a/contrib/pgcrypto/px-crypt.h b/contrib/pgcrypto/px-crypt.h index 3829ec6c347..bb03cf5592a 100644 --- a/contrib/pgcrypto/px-crypt.h +++ b/contrib/pgcrypto/px-crypt.h @@ -61,13 +61,13 @@ #define PX_SHACRYPT_BUF_LEN (PX_SHACRYPT_SALT_BUF_LEN + 86 + 1) /* Default number of rounds of shacrypt if not explicitly specified. */ -#define PX_SHACRYPT_ROUNDS_DEFAULT 5000l +#define PX_SHACRYPT_ROUNDS_DEFAULT 5000 /* Minimum number of rounds of shacrypt. */ -#define PX_SHACRYPT_ROUNDS_MIN 1000l +#define PX_SHACRYPT_ROUNDS_MIN 1000 /* Maximum number of rounds of shacrypt. */ -#define PX_SHACRYPT_ROUNDS_MAX 999999999l +#define PX_SHACRYPT_ROUNDS_MAX 999999999 /* * main interface -- 2.39.5 --zih5rdnpxe7pfzuf-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v22 2/2] Add REPACK command @ 2025-07-26 17:57 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2025-07-26 17:57 UTC (permalink / raw) REPACK absorbs the functionality of VACUUM FULL and CLUSTER in a single command. Because this functionality is completely different from regular VACUUM, having it separate from VACUUM makes it easier for users to understand; as for CLUSTER, the term is heavily overloaded in the TI world and even in Postgres itself, so it's good that we can avoid it. This also adds pg_repackdb, a new utility that can invoke the new commands. This is heavily based on vacuumdb. We may still change the implementation, depending on how does Windows like this one. Author: Antonin Houska <ah@cybertec.at> Reviewed-by: To fill in Discussion: https://postgr.es/m/82651.1720540558@antos Discussion: https://postgr.es/m/202507262156.sb455angijk6@alvherre.pgsql --- doc/src/sgml/monitoring.sgml | 223 ++++++- doc/src/sgml/ref/allfiles.sgml | 2 + doc/src/sgml/ref/cluster.sgml | 97 +-- doc/src/sgml/ref/clusterdb.sgml | 5 + doc/src/sgml/ref/pg_repackdb.sgml | 479 ++++++++++++++ doc/src/sgml/ref/repack.sgml | 284 +++++++++ doc/src/sgml/ref/vacuum.sgml | 33 +- doc/src/sgml/reference.sgml | 2 + src/backend/access/heap/heapam_handler.c | 32 +- src/backend/catalog/index.c | 2 +- src/backend/catalog/system_views.sql | 26 + src/backend/commands/cluster.c | 758 +++++++++++++++-------- src/backend/commands/vacuum.c | 3 +- src/backend/parser/gram.y | 88 ++- src/backend/tcop/utility.c | 20 +- src/backend/utils/adt/pgstatfuncs.c | 2 + src/bin/psql/tab-complete.in.c | 33 +- src/bin/scripts/Makefile | 4 +- src/bin/scripts/meson.build | 2 + src/bin/scripts/pg_repackdb.c | 238 +++++++ src/bin/scripts/t/103_repackdb.pl | 24 + src/bin/scripts/vacuuming.c | 97 ++- src/bin/scripts/vacuuming.h | 9 + src/include/commands/cluster.h | 8 +- src/include/commands/progress.h | 61 +- src/include/nodes/parsenodes.h | 20 +- src/include/parser/kwlist.h | 1 + src/include/tcop/cmdtaglist.h | 1 + src/include/utils/backend_progress.h | 1 + src/test/regress/expected/cluster.out | 125 +++- src/test/regress/expected/rules.out | 23 + src/test/regress/sql/cluster.sql | 59 ++ src/tools/pgindent/typedefs.list | 3 + 33 files changed, 2318 insertions(+), 447 deletions(-) create mode 100644 doc/src/sgml/ref/pg_repackdb.sgml create mode 100644 doc/src/sgml/ref/repack.sgml create mode 100644 src/bin/scripts/pg_repackdb.c create mode 100644 src/bin/scripts/t/103_repackdb.pl diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 3f4a27a736e..12e103d319d 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -405,6 +405,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser </entry> </row> + <row> + <entry><structname>pg_stat_progress_repack</structname><indexterm><primary>pg_stat_progress_repack</primary></indexterm></entry> + <entry>One row for each backend running + <command>REPACK</command>, showing current progress. See + <xref linkend="repack-progress-reporting"/>. + </entry> + </row> + <row> <entry><structname>pg_stat_progress_basebackup</structname><indexterm><primary>pg_stat_progress_basebackup</primary></indexterm></entry> <entry>One row for each WAL sender process streaming a base backup, @@ -5506,7 +5514,8 @@ FROM pg_stat_get_backend_idset() AS backendid; certain commands during command execution. Currently, the only commands which support progress reporting are <command>ANALYZE</command>, <command>CLUSTER</command>, - <command>CREATE INDEX</command>, <command>VACUUM</command>, + <command>CREATE INDEX</command>, <command>REPACK</command>, + <command>VACUUM</command>, <command>COPY</command>, and <xref linkend="protocol-replication-base-backup"/> (i.e., replication command that <xref linkend="app-pgbasebackup"/> issues to take @@ -5965,6 +5974,218 @@ FROM pg_stat_get_backend_idset() AS backendid; </table> </sect2> + <sect2 id="repack-progress-reporting"> + <title>REPACK Progress Reporting</title> + + <indexterm> + <primary>pg_stat_progress_repack</primary> + </indexterm> + + <para> + Whenever <command>REPACK</command> is running, + the <structname>pg_stat_progress_repack</structname> view will contain a + row for each backend that is currently running the command. The tables + below describe the information that will be reported and provide + information about how to interpret it. + </para> + + <table id="pg-stat-progress-repack-view" xreflabel="pg_stat_progress_repack"> + <title><structname>pg_stat_progress_repack</structname> View</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + Column Type + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>pid</structfield> <type>integer</type> + </para> + <para> + Process ID of backend. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>datid</structfield> <type>oid</type> + </para> + <para> + OID of the database to which this backend is connected. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>datname</structfield> <type>name</type> + </para> + <para> + Name of the database to which this backend is connected. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>relid</structfield> <type>oid</type> + </para> + <para> + OID of the table being repacked. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>phase</structfield> <type>text</type> + </para> + <para> + Current processing phase. See <xref linkend="repack-phases"/>. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>repack_index_relid</structfield> <type>oid</type> + </para> + <para> + If the table is being scanned using an index, this is the OID of the + index being used; otherwise, it is zero. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>heap_tuples_scanned</structfield> <type>bigint</type> + </para> + <para> + Number of heap tuples scanned. + This counter only advances when the phase is + <literal>seq scanning heap</literal>, + <literal>index scanning heap</literal> + or <literal>writing new heap</literal>. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>heap_tuples_written</structfield> <type>bigint</type> + </para> + <para> + Number of heap tuples written. + This counter only advances when the phase is + <literal>seq scanning heap</literal>, + <literal>index scanning heap</literal> + or <literal>writing new heap</literal>. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>heap_blks_total</structfield> <type>bigint</type> + </para> + <para> + Total number of heap blocks in the table. This number is reported + as of the beginning of <literal>seq scanning heap</literal>. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>heap_blks_scanned</structfield> <type>bigint</type> + </para> + <para> + Number of heap blocks scanned. This counter only advances when the + phase is <literal>seq scanning heap</literal>. + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>index_rebuild_count</structfield> <type>bigint</type> + </para> + <para> + Number of indexes rebuilt. This counter only advances when the phase + is <literal>rebuilding index</literal>. + </para></entry> + </row> + </tbody> + </tgroup> + </table> + + <table id="repack-phases"> + <title>REPACK Phases</title> + <tgroup cols="2"> + <colspec colname="col1" colwidth="1*"/> + <colspec colname="col2" colwidth="2*"/> + <thead> + <row> + <entry>Phase</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>initializing</literal></entry> + <entry> + The command is preparing to begin scanning the heap. This phase is + expected to be very brief. + </entry> + </row> + <row> + <entry><literal>seq scanning heap</literal></entry> + <entry> + The command is currently scanning the table using a sequential scan. + </entry> + </row> + <row> + <entry><literal>index scanning heap</literal></entry> + <entry> + <command>REPACK</command> is currently scanning the table using an index scan. + </entry> + </row> + <row> + <entry><literal>sorting tuples</literal></entry> + <entry> + <command>REPACK</command> is currently sorting tuples. + </entry> + </row> + <row> + <entry><literal>writing new heap</literal></entry> + <entry> + <command>REPACK</command> is currently writing the new heap. + </entry> + </row> + <row> + <entry><literal>swapping relation files</literal></entry> + <entry> + The command is currently swapping newly-built files into place. + </entry> + </row> + <row> + <entry><literal>rebuilding index</literal></entry> + <entry> + The command is currently rebuilding an index. + </entry> + </row> + <row> + <entry><literal>performing final cleanup</literal></entry> + <entry> + The command is performing final cleanup. When this phase is + completed, <command>REPACK</command> will end. + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect2> + <sect2 id="copy-progress-reporting"> <title>COPY Progress Reporting</title> diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index f5be638867a..eabf92e3536 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -167,6 +167,7 @@ Complete list of usable sgml source files in this directory. <!ENTITY refreshMaterializedView SYSTEM "refresh_materialized_view.sgml"> <!ENTITY reindex SYSTEM "reindex.sgml"> <!ENTITY releaseSavepoint SYSTEM "release_savepoint.sgml"> +<!ENTITY repack SYSTEM "repack.sgml"> <!ENTITY reset SYSTEM "reset.sgml"> <!ENTITY revoke SYSTEM "revoke.sgml"> <!ENTITY rollback SYSTEM "rollback.sgml"> @@ -212,6 +213,7 @@ Complete list of usable sgml source files in this directory. <!ENTITY pgIsready SYSTEM "pg_isready.sgml"> <!ENTITY pgReceivewal SYSTEM "pg_receivewal.sgml"> <!ENTITY pgRecvlogical SYSTEM "pg_recvlogical.sgml"> +<!ENTITY pgRepackdb SYSTEM "pg_repackdb.sgml"> <!ENTITY pgResetwal SYSTEM "pg_resetwal.sgml"> <!ENTITY pgRestore SYSTEM "pg_restore.sgml"> <!ENTITY pgRewind SYSTEM "pg_rewind.sgml"> diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml index 8811f169ea0..cfcfb65e349 100644 --- a/doc/src/sgml/ref/cluster.sgml +++ b/doc/src/sgml/ref/cluster.sgml @@ -33,51 +33,13 @@ CLUSTER [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r <title>Description</title> <para> - <command>CLUSTER</command> instructs <productname>PostgreSQL</productname> - to cluster the table specified - by <replaceable class="parameter">table_name</replaceable> - based on the index specified by - <replaceable class="parameter">index_name</replaceable>. The index must - already have been defined on - <replaceable class="parameter">table_name</replaceable>. + The <command>CLUSTER</command> command is equivalent to + <xref linkend="sql-repack"/> with an <literal>USING INDEX</literal> + clause. See there for more details. </para> - <para> - When a table is clustered, it is physically reordered - based on the index information. Clustering is a one-time operation: - when the table is subsequently updated, the changes are - not clustered. That is, no attempt is made to store new or - updated rows according to their index order. (If one wishes, one can - periodically recluster by issuing the command again. Also, setting - the table's <literal>fillfactor</literal> storage parameter to less than - 100% can aid in preserving cluster ordering during updates, since updated - rows are kept on the same page if enough space is available there.) - </para> +<!-- Do we need to describe exactly which options map to what? They seem obvious to me. --> - <para> - When a table is clustered, <productname>PostgreSQL</productname> - remembers which index it was clustered by. The form - <command>CLUSTER <replaceable class="parameter">table_name</replaceable></command> - reclusters the table using the same index as before. You can also - use the <literal>CLUSTER</literal> or <literal>SET WITHOUT CLUSTER</literal> - forms of <link linkend="sql-altertable"><command>ALTER TABLE</command></link> to set the index to be used for - future cluster operations, or to clear any previous setting. - </para> - - <para> - <command>CLUSTER</command> without a - <replaceable class="parameter">table_name</replaceable> reclusters all the - previously-clustered tables in the current database that the calling user - has privileges for. This form of <command>CLUSTER</command> cannot be - executed inside a transaction block. - </para> - - <para> - When a table is being clustered, an <literal>ACCESS - EXCLUSIVE</literal> lock is acquired on it. This prevents any other - database operations (both reads and writes) from operating on the - table until the <command>CLUSTER</command> is finished. - </para> </refsect1> <refsect1> @@ -136,63 +98,12 @@ CLUSTER [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r on the table. </para> - <para> - In cases where you are accessing single rows randomly - within a table, the actual order of the data in the - table is unimportant. However, if you tend to access some - data more than others, and there is an index that groups - them together, you will benefit from using <command>CLUSTER</command>. - If you are requesting a range of indexed values from a table, or a - single indexed value that has multiple rows that match, - <command>CLUSTER</command> will help because once the index identifies the - table page for the first row that matches, all other rows - that match are probably already on the same table page, - and so you save disk accesses and speed up the query. - </para> - - <para> - <command>CLUSTER</command> can re-sort the table using either an index scan - on the specified index, or (if the index is a b-tree) a sequential - scan followed by sorting. It will attempt to choose the method that - will be faster, based on planner cost parameters and available statistical - information. - </para> - <para> While <command>CLUSTER</command> is running, the <xref linkend="guc-search-path"/> is temporarily changed to <literal>pg_catalog, pg_temp</literal>. </para> - <para> - When an index scan is used, a temporary copy of the table is created that - contains the table data in the index order. Temporary copies of each - index on the table are created as well. Therefore, you need free space on - disk at least equal to the sum of the table size and the index sizes. - </para> - - <para> - When a sequential scan and sort is used, a temporary sort file is - also created, so that the peak temporary space requirement is as much - as double the table size, plus the index sizes. This method is often - faster than the index scan method, but if the disk space requirement is - intolerable, you can disable this choice by temporarily setting <xref - linkend="guc-enable-sort"/> to <literal>off</literal>. - </para> - - <para> - It is advisable to set <xref linkend="guc-maintenance-work-mem"/> to - a reasonably large value (but not more than the amount of RAM you can - dedicate to the <command>CLUSTER</command> operation) before clustering. - </para> - - <para> - Because the planner records statistics about the ordering of - tables, it is advisable to run <link linkend="sql-analyze"><command>ANALYZE</command></link> - on the newly clustered table. - Otherwise, the planner might make poor choices of query plans. - </para> - <para> Because <command>CLUSTER</command> remembers which indexes are clustered, one can cluster the tables one wants clustered manually the first time, diff --git a/doc/src/sgml/ref/clusterdb.sgml b/doc/src/sgml/ref/clusterdb.sgml index 0d2051bf6f1..546c1289c31 100644 --- a/doc/src/sgml/ref/clusterdb.sgml +++ b/doc/src/sgml/ref/clusterdb.sgml @@ -64,6 +64,11 @@ PostgreSQL documentation this utility and via other methods for accessing the server. </para> + <para> + <application>clusterdb</application> has been superceded by + <application>pg_repackdb</application>. + </para> + </refsect1> diff --git a/doc/src/sgml/ref/pg_repackdb.sgml b/doc/src/sgml/ref/pg_repackdb.sgml new file mode 100644 index 00000000000..32570d071cb --- /dev/null +++ b/doc/src/sgml/ref/pg_repackdb.sgml @@ -0,0 +1,479 @@ +<!-- +doc/src/sgml/ref/pg_repackdb.sgml +PostgreSQL documentation +--> + +<refentry id="app-pgrepackdb"> + <indexterm zone="app-pgrepackdb"> + <primary>pg_repackdb</primary> + </indexterm> + + <refmeta> + <refentrytitle><application>pg_repackdb</application></refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo>Application</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>pg_repackdb</refname> + <refpurpose>repack and analyze a <productname>PostgreSQL</productname> + database</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>pg_repackdb</command> + <arg rep="repeat"><replaceable>connection-option</replaceable></arg> + <arg rep="repeat"><replaceable>option</replaceable></arg> + + <arg choice="plain" rep="repeat"> + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><option>-t</option></arg> + <arg choice="plain"><option>--table</option></arg> + </group> + <replaceable>table</replaceable> + <arg choice="opt">( <replaceable class="parameter">column</replaceable> [,...] )</arg> + </arg> + </arg> + + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><replaceable>dbname</replaceable></arg> + <arg choice="plain"><option>-a</option></arg> + <arg choice="plain"><option>--all</option></arg> + </group> + </arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>pg_repackdb</command> + <arg rep="repeat"><replaceable>connection-option</replaceable></arg> + <arg rep="repeat"><replaceable>option</replaceable></arg> + + <arg choice="plain" rep="repeat"> + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><option>-n</option></arg> + <arg choice="plain"><option>--schema</option></arg> + </group> + <replaceable>schema</replaceable> + </arg> + </arg> + + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><replaceable>dbname</replaceable></arg> + <arg choice="plain"><option>-a</option></arg> + <arg choice="plain"><option>--all</option></arg> + </group> + </arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>pg_repackdb</command> + <arg rep="repeat"><replaceable>connection-option</replaceable></arg> + <arg rep="repeat"><replaceable>option</replaceable></arg> + + <arg choice="plain" rep="repeat"> + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><option>-N</option></arg> + <arg choice="plain"><option>--exclude-schema</option></arg> + </group> + <replaceable>schema</replaceable> + </arg> + </arg> + + <arg choice="opt"> + <group choice="plain"> + <arg choice="plain"><replaceable>dbname</replaceable></arg> + <arg choice="plain"><option>-a</option></arg> + <arg choice="plain"><option>--all</option></arg> + </group> + </arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <application>pg_repackdb</application> is a utility for repacking a + <productname>PostgreSQL</productname> database. + <application>pg_repackdb</application> will also generate internal + statistics used by the <productname>PostgreSQL</productname> query + optimizer. + </para> + + <para> + <application>pg_repackdb</application> is a wrapper around the SQL + command <link linkend="sql-repack"><command>REPACK</command></link> There + is no effective difference between repacking and analyzing databases via + this utility and via other methods for accessing the server. + </para> + + </refsect1> + + + <refsect1> + <title>Options</title> + + <para> + <application>pg_repackdb</application> accepts the following command-line arguments: + <variablelist> + <varlistentry> + <term><option>-a</option></term> + <term><option>--all</option></term> + <listitem> + <para> + Repack all databases. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option><optional>-d</optional> <replaceable class="parameter">dbname</replaceable></option></term> + <term><option><optional>--dbname=</optional><replaceable class="parameter">dbname</replaceable></option></term> + <listitem> + <para> + Specifies the name of the database to be repacked or analyzed, + when <option>-a</option>/<option>--all</option> is not used. If this + is not specified, the database name is read from the environment + variable <envar>PGDATABASE</envar>. If that is not set, the user name + specified for the connection is used. + The <replaceable>dbname</replaceable> can be + a <link linkend="libpq-connstring">connection string</link>. If so, + connection string parameters will override any conflicting command + line options. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-e</option></term> + <term><option>--echo</option></term> + <listitem> + <para> + Echo the commands that <application>pg_repackdb</application> + generates and sends to the server. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-j <replaceable class="parameter">njobs</replaceable></option></term> + <term><option>--jobs=<replaceable class="parameter">njobs</replaceable></option></term> + <listitem> + <para> + Execute the repack or analyze commands in parallel by running + <replaceable class="parameter">njobs</replaceable> + commands simultaneously. This option may reduce the processing time + but it also increases the load on the database server. + </para> + <para> + <application>pg_repackdb</application> will open + <replaceable class="parameter">njobs</replaceable> connections to the + database, so make sure your <xref linkend="guc-max-connections"/> + setting is high enough to accommodate all connections. + </para> + <para> + Note that using this mode might cause deadlock failures if certain + system catalogs are processed in parallel. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-n <replaceable class="parameter">schema</replaceable></option></term> + <term><option>--schema=<replaceable class="parameter">schema</replaceable></option></term> + <listitem> + <para> + Repack or analyze all tables in + <replaceable class="parameter">schema</replaceable> only. Multiple + schemas can be repacked by writing multiple <option>-n</option> + switches. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-N <replaceable class="parameter">schema</replaceable></option></term> + <term><option>--exclude-schema=<replaceable class="parameter">schema</replaceable></option></term> + <listitem> + <para> + Do not repack or analyze any tables in + <replaceable class="parameter">schema</replaceable>. Multiple schemas + can be excluded by writing multiple <option>-N</option> switches. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-q</option></term> + <term><option>--quiet</option></term> + <listitem> + <para> + Do not display progress messages. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-t <replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</option></term> + <term><option>--table=<replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</option></term> + <listitem> + <para> + Repack or analyze <replaceable class="parameter">table</replaceable> + only. Column names can be specified only in conjunction with + the <option>--analyze</option> option. Multiple tables can be + repacked by writing multiple + <option>-t</option> switches. + </para> + <tip> + <para> + If you specify columns, you probably have to escape the parentheses + from the shell. (See examples below.) + </para> + </tip> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-v</option></term> + <term><option>--verbose</option></term> + <listitem> + <para> + Print detailed information during processing. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-V</option></term> + <term><option>--version</option></term> + <listitem> + <para> + Print the <application>pg_repackdb</application> version and exit. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-z</option></term> + <term><option>--analyze</option></term> + <listitem> + <para> + Also calculate statistics for use by the optimizer. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-?</option></term> + <term><option>--help</option></term> + <listitem> + <para> + Show help about <application>pg_repackdb</application> command line + arguments, and exit. + </para> + </listitem> + </varlistentry> + + </variablelist> + </para> + + <para> + <application>pg_repackdb</application> also accepts + the following command-line arguments for connection parameters: + <variablelist> + <varlistentry> + <term><option>-h <replaceable class="parameter">host</replaceable></option></term> + <term><option>--host=<replaceable class="parameter">host</replaceable></option></term> + <listitem> + <para> + Specifies the host name of the machine on which the server + is running. If the value begins with a slash, it is used + as the directory for the Unix domain socket. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-p <replaceable class="parameter">port</replaceable></option></term> + <term><option>--port=<replaceable class="parameter">port</replaceable></option></term> + <listitem> + <para> + Specifies the TCP port or local Unix domain socket file + extension on which the server + is listening for connections. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-U <replaceable class="parameter">username</replaceable></option></term> + <term><option>--username=<replaceable class="parameter">username</replaceable></option></term> + <listitem> + <para> + User name to connect as. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-w</option></term> + <term><option>--no-password</option></term> + <listitem> + <para> + Never issue a password prompt. If the server requires + password authentication and a password is not available by + other means such as a <filename>.pgpass</filename> file, the + connection attempt will fail. This option can be useful in + batch jobs and scripts where no user is present to enter a + password. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-W</option></term> + <term><option>--password</option></term> + <listitem> + <para> + Force <application>pg_repackdb</application> to prompt for a + password before connecting to a database. + </para> + + <para> + This option is never essential, since + <application>pg_repackdb</application> will automatically prompt + for a password if the server demands password authentication. + However, <application>pg_repackdb</application> will waste a + connection attempt finding out that the server wants a password. + In some cases it is worth typing <option>-W</option> to avoid the extra + connection attempt. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--maintenance-db=<replaceable class="parameter">dbname</replaceable></option></term> + <listitem> + <para> + When the <option>-a</option>/<option>--all</option> is used, connect + to this database to gather the list of databases to repack. + If not specified, the <literal>postgres</literal> database will be used, + or if that does not exist, <literal>template1</literal> will be used. + This can be a <link linkend="libpq-connstring">connection + string</link>. If so, connection string parameters will override any + conflicting command line options. Also, connection string parameters + other than the database name itself will be re-used when connecting + to other databases. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </refsect1> + + + <refsect1> + <title>Environment</title> + + <variablelist> + <varlistentry> + <term><envar>PGDATABASE</envar></term> + <term><envar>PGHOST</envar></term> + <term><envar>PGPORT</envar></term> + <term><envar>PGUSER</envar></term> + + <listitem> + <para> + Default connection parameters + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><envar>PG_COLOR</envar></term> + <listitem> + <para> + Specifies whether to use color in diagnostic messages. Possible values + are <literal>always</literal>, <literal>auto</literal> and + <literal>never</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> + + <para> + This utility, like most other <productname>PostgreSQL</productname> utilities, + also uses the environment variables supported by <application>libpq</application> + (see <xref linkend="libpq-envars"/>). + </para> + + </refsect1> + + + <refsect1> + <title>Diagnostics</title> + + <para> + In case of difficulty, see + <xref linkend="sql-repack"/> and <xref linkend="app-psql"/> for + discussions of potential problems and error messages. + The database server must be running at the + targeted host. Also, any default connection settings and environment + variables used by the <application>libpq</application> front-end + library will apply. + </para> + + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + To repack the database <literal>test</literal>: +<screen> +<prompt>$ </prompt><userinput>pg_repackdb test</userinput> +</screen> + </para> + + <para> + To repack and analyze for the optimizer a database named + <literal>bigdb</literal>: +<screen> +<prompt>$ </prompt><userinput>pg_repackdb --analyze bigdb</userinput> +</screen> + </para> + + <para> + To repack a single table + <literal>foo</literal> in a database named + <literal>xyzzy</literal>, and analyze a single column + <literal>bar</literal> of the table for the optimizer: +<screen> +<prompt>$ </prompt><userinput>pg_repackdb --analyze --verbose --table='foo(bar)' xyzzy</userinput> +</screen></para> + + <para> + To repack all tables in the <literal>foo</literal> and <literal>bar</literal> schemas + in a database named <literal>xyzzy</literal>: +<screen> +<prompt>$ </prompt><userinput>pg_repackdb --schema='foo' --schema='bar' xyzzy</userinput> +</screen></para> + + + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-repack"/></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml new file mode 100644 index 00000000000..fd9d89f8aaa --- /dev/null +++ b/doc/src/sgml/ref/repack.sgml @@ -0,0 +1,284 @@ +<!-- +doc/src/sgml/ref/repack.sgml +PostgreSQL documentation +--> + +<refentry id="sql-repack"> + <indexterm zone="sql-repack"> + <primary>REPACK</primary> + </indexterm> + + <refmeta> + <refentrytitle>REPACK</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>REPACK</refname> + <refpurpose>rewrite a table to reclaim disk space</refpurpose> + </refnamediv> + + <refsynopsisdiv> +<synopsis> +REPACK [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <replaceable class="parameter">table_name</replaceable> [ USING INDEX [ <replaceable class="parameter">index_name</replaceable> ] ] ] + +<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase> + + VERBOSE [ <replaceable class="parameter">boolean</replaceable> ] + ANALYSE | ANALYZE +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>REPACK</command> reclaims storage occupied by dead + tuples. Unlike <command>VACUUM</command>, it does so by rewriting the + entire contents of the table specified + by <replaceable class="parameter">table_name</replaceable> into a new disk + file with no extra space (except for the space guaranteed by + the <literal>fillfactor</literal> storage parameter), allowing unused space + to be returned to the operating system. + </para> + + <para> + Without + a <replaceable class="parameter">table_name</replaceable>, <command>REPACK</command> + processes every table and materialized view in the current database that + the current user has the <literal>MAINTAIN</literal> privilege on. This + form of <command>REPACK</command> cannot be executed inside a transaction + block. + </para> + + <para> + If a <literal>USING INDEX</literal> clause is specified, the rows are + physically reordered based on information from an index. Please see the + notes on clustering below. + </para> + + <para> + When a table is being repacked, an <literal>ACCESS EXCLUSIVE</literal> lock + is acquired on it. This prevents any other database operations (both reads + and writes) from operating on the table until the <command>REPACK</command> + is finished. + </para> + + <refsect2 id="sql-repack-notes-on-clustering" xreflabel="Notes on Clustering"> + <title>Notes on Clustering</title> + + <para> + If the <literal>USING INDEX</literal> clause is specified, the rows in + the table are physically reordered following an index: if an index name + is specified in the command, then that index is used; if no index name + is specified, then the index that has been configured as the index to + cluster on. If no index has been configured in this way, an error is + thrown. The index given in the <literal>USING INDEX</literal> clause + is configured as the index to cluster on, as well as an index given + to the <command>CLUSTER</command> command. An index can be set + manually using <command>ALTER TABLE ... CLUSTER ON</command>, and reset + with <command>ALTER TABLE ... SET WITHOUT CLUSTER</command>. + </para> + + <para> + If no table name is specified in <command>REPACK USING INDEX</command>, + all tables which have a clustering index defined and which the calling + user has privileges for are processed. + </para> + + <para> + Clustering is a one-time operation: when the table is + subsequently updated, the changes are not clustered. That is, no attempt + is made to store new or updated rows according to their index order. (If + one wishes, one can periodically recluster by issuing the command again. + Also, setting the table's <literal>fillfactor</literal> storage parameter + to less than 100% can aid in preserving cluster ordering during updates, + since updated rows are kept on the same page if enough space is available + there.) + </para> + + <para> + In cases where you are accessing single rows randomly within a table, the + actual order of the data in the table is unimportant. However, if you tend + to access some data more than others, and there is an index that groups + them together, you will benefit from using clustering. If + you are requesting a range of indexed values from a table, or a single + indexed value that has multiple rows that match, + <command>REPACK</command> will help because once the index identifies the + table page for the first row that matches, all other rows that match are + probably already on the same table page, and so you save disk accesses and + speed up the query. + </para> + + <para> + <command>REPACK</command> can re-sort the table using either an index scan + on the specified index (if the index is a b-tree), or a sequential scan + followed by sorting. It will attempt to choose the method that will be + faster, based on planner cost parameters and available statistical + information. + </para> + + <para> + Because the planner records statistics about the ordering of tables, it is + advisable to + run <link linkend="sql-analyze"><command>ANALYZE</command></link> on the + newly repacked table. Otherwise, the planner might make poor choices of + query plans. + </para> + </refsect2> + + <refsect2 id="sql-repack-notes-on-resources" xreflabel="Notes on Resources"> + <title>Notes on Resources</title> + + <para> + When an index scan or a sequential scan without sort is used, a temporary + copy of the table is created that contains the table data in the index + order. Temporary copies of each index on the table are created as well. + Therefore, you need free space on disk at least equal to the sum of the + table size and the index sizes. + </para> + + <para> + When a sequential scan and sort is used, a temporary sort file is also + created, so that the peak temporary space requirement is as much as double + the table size, plus the index sizes. This method is often faster than + the index scan method, but if the disk space requirement is intolerable, + you can disable this choice by temporarily setting + <xref linkend="guc-enable-sort"/> to <literal>off</literal>. + </para> + + <para> + It is advisable to set <xref linkend="guc-maintenance-work-mem"/> to a + reasonably large value (but not more than the amount of RAM you can + dedicate to the <command>REPACK</command> operation) before repacking. + </para> + </refsect2> + + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">table_name</replaceable></term> + <listitem> + <para> + The name (possibly schema-qualified) of a table. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">index_name</replaceable></term> + <listitem> + <para> + The name of an index. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>VERBOSE</literal></term> + <listitem> + <para> + Prints a progress report as each table is repacked + at <literal>INFO</literal> level. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>ANALYZE</literal></term> + <term><literal>ANALYSE</literal></term> + <listitem> + <para> + Applies <xref linkend="sql-analyze"/> on the table after repacking. This is + currently only supported when a single (non-partitioned) table is specified. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">boolean</replaceable></term> + <listitem> + <para> + Specifies whether the selected option should be turned on or off. + You can write <literal>TRUE</literal>, <literal>ON</literal>, or + <literal>1</literal> to enable the option, and <literal>FALSE</literal>, + <literal>OFF</literal>, or <literal>0</literal> to disable it. The + <replaceable class="parameter">boolean</replaceable> value can also + be omitted, in which case <literal>TRUE</literal> is assumed. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para> + To repack a table, one must have the <literal>MAINTAIN</literal> privilege + on the table. + </para> + + <para> + While <command>REPACK</command> is running, the <xref + linkend="guc-search-path"/> is temporarily changed to <literal>pg_catalog, + pg_temp</literal>. + </para> + + <para> + Each backend running <command>REPACK</command> will report its progress + in the <structname>pg_stat_progress_repack</structname> view. See + <xref linkend="repack-progress-reporting"/> for details. + </para> + + <para> + Repacking a partitioned table repacks each of its partitions. If an index + is specified, each partition is repacked using the partition of that + index. <command>REPACK</command> on a partitioned table cannot be executed + inside a transaction block. + </para> + + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Repack the table <literal>employees</literal>: +<programlisting> +REPACK employees; +</programlisting> + </para> + + <para> + Repack the table <literal>employees</literal> on the basis of its + index <literal>employees_ind</literal> (Since index is used here, this is + effectively clustering): +<programlisting> +REPACK employees USING INDEX employees_ind; +</programlisting> + </para> + + <para> + Repack all tables in the database on which you have + the <literal>MAINTAIN</literal> privilege: +<programlisting> +REPACK; +</programlisting></para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + There is no <command>REPACK</command> statement in the SQL standard. + </para> + + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index bd5dcaf86a5..062b658cfcd 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -25,7 +25,6 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase> - FULL [ <replaceable class="parameter">boolean</replaceable> ] FREEZE [ <replaceable class="parameter">boolean</replaceable> ] VERBOSE [ <replaceable class="parameter">boolean</replaceable> ] ANALYZE [ <replaceable class="parameter">boolean</replaceable> ] @@ -39,6 +38,7 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re SKIP_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ] ONLY_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ] BUFFER_USAGE_LIMIT <replaceable class="parameter">size</replaceable> + FULL [ <replaceable class="parameter">boolean</replaceable> ] <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase> @@ -95,20 +95,6 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re <title>Parameters</title> <variablelist> - <varlistentry> - <term><literal>FULL</literal></term> - <listitem> - <para> - Selects <quote>full</quote> vacuum, which can reclaim more - space, but takes much longer and exclusively locks the table. - This method also requires extra disk space, since it writes a - new copy of the table and doesn't release the old copy until - the operation is complete. Usually this should only be used when a - significant amount of space needs to be reclaimed from within the table. - </para> - </listitem> - </varlistentry> - <varlistentry> <term><literal>FREEZE</literal></term> <listitem> @@ -362,6 +348,23 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re </listitem> </varlistentry> + <varlistentry> + <term><literal>FULL</literal></term> + <listitem> + <para> + This option, which is deprecated, makes <command>VACUUM</command> + behave like <command>REPACK</command> without a + <literal>USING INDEX</literal> clause. + This method of compacting the table takes much longer than + <command>VACUUM</command> and exclusively locks the table. + This method also requires extra disk space, since it writes a + new copy of the table and doesn't release the old copy until + the operation is complete. Usually this should only be used when a + significant amount of space needs to be reclaimed from within the table. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><replaceable class="parameter">boolean</replaceable></term> <listitem> diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index ff85ace83fc..2ee08e21f41 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -195,6 +195,7 @@ &refreshMaterializedView; &reindex; &releaseSavepoint; + &repack; &reset; &revoke; &rollback; @@ -257,6 +258,7 @@ &pgIsready; &pgReceivewal; &pgRecvlogical; + &pgRepackdb; &pgRestore; &pgVerifyBackup; &psqlRef; diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index bcbac844bb6..79f9de5d760 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -741,13 +741,13 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, if (OldIndex != NULL && !use_sort) { const int ci_index[] = { - PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_INDEX_RELID + PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_INDEX_RELID }; int64 ci_val[2]; /* Set phase and OIDOldIndex to columns */ - ci_val[0] = PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP; + ci_val[0] = PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP; ci_val[1] = RelationGetRelid(OldIndex); pgstat_progress_update_multi_param(2, ci_index, ci_val); @@ -759,15 +759,15 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, else { /* In scan-and-sort mode and also VACUUM FULL, set phase */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP); tableScan = table_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL); heapScan = (HeapScanDesc) tableScan; indexScan = NULL; /* Set total heap blocks */ - pgstat_progress_update_param(PROGRESS_CLUSTER_TOTAL_HEAP_BLKS, + pgstat_progress_update_param(PROGRESS_REPACK_TOTAL_HEAP_BLKS, heapScan->rs_nblocks); } @@ -809,7 +809,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, * is manually updated to the correct value when the table * scan finishes. */ - pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED, + pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED, heapScan->rs_nblocks); break; } @@ -825,7 +825,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, */ if (prev_cblock != heapScan->rs_cblock) { - pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_BLKS_SCANNED, + pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED, (heapScan->rs_cblock + heapScan->rs_nblocks - heapScan->rs_startblock @@ -912,14 +912,14 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, * In scan-and-sort mode, report increase in number of tuples * scanned */ - pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED, + pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_SCANNED, *num_tuples); } else { const int ct_index[] = { - PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED, - PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN + PROGRESS_REPACK_HEAP_TUPLES_SCANNED, + PROGRESS_REPACK_HEAP_TUPLES_WRITTEN }; int64 ct_val[2]; @@ -952,14 +952,14 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, double n_tuples = 0; /* Report that we are now sorting tuples */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_SORT_TUPLES); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_SORT_TUPLES); tuplesort_performsort(tuplesort); /* Report that we are now writing new heap */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP); for (;;) { @@ -977,7 +977,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, values, isnull, rwstate); /* Report n_tuples */ - pgstat_progress_update_param(PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN, + pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_WRITTEN, n_tuples); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 5d9db167e59..08d4b8e44d7 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -4079,7 +4079,7 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, Assert(!ReindexIsProcessingIndex(indexOid)); /* Set index rebuild count */ - pgstat_progress_update_param(PROGRESS_CLUSTER_INDEX_REBUILD_COUNT, + pgstat_progress_update_param(PROGRESS_REPACK_INDEX_REBUILD_COUNT, i); i++; } diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index c77fa0234bb..7924922e0fe 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1279,6 +1279,32 @@ CREATE VIEW pg_stat_progress_cluster AS FROM pg_stat_get_progress_info('CLUSTER') AS S LEFT JOIN pg_database D ON S.datid = D.oid; +CREATE VIEW pg_stat_progress_repack AS + SELECT + S.pid AS pid, + S.datid AS datid, + D.datname AS datname, + S.relid AS relid, + -- param1 is currently unused + CASE S.param2 WHEN 0 THEN 'initializing' + WHEN 1 THEN 'seq scanning heap' + WHEN 2 THEN 'index scanning heap' + WHEN 3 THEN 'sorting tuples' + WHEN 4 THEN 'writing new heap' + WHEN 5 THEN 'swapping relation files' + WHEN 6 THEN 'rebuilding index' + WHEN 7 THEN 'performing final cleanup' + END AS phase, + CAST(S.param3 AS oid) AS repack_index_relid, + S.param4 AS heap_tuples_scanned, + S.param5 AS heap_tuples_written, + S.param6 AS heap_blks_total, + S.param7 AS heap_blks_scanned, + S.param8 AS index_rebuild_count + FROM pg_stat_get_progress_info('REPACK') AS S + LEFT JOIN pg_database D ON S.datid = D.oid; + + CREATE VIEW pg_stat_progress_create_index AS SELECT S.pid AS pid, S.datid AS datid, D.datname AS datname, diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index b55221d44cd..8b64f9e6795 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -67,18 +67,41 @@ typedef struct Oid indexOid; } RelToCluster; - -static void cluster_multiple_rels(List *rtcs, ClusterParams *params); -static void rebuild_relation(Relation OldHeap, Relation index, bool verbose); +static bool cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, + Oid indexOid, Oid userid, int options); +static void rebuild_relation(RepackCommand cmd, bool usingindex, + Relation OldHeap, Relation index, bool verbose); static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti); -static List *get_tables_to_cluster(MemoryContext cluster_context); -static List *get_tables_to_cluster_partitioned(MemoryContext cluster_context, - Oid indexOid); -static bool cluster_is_permitted_for_relation(Oid relid, Oid userid); +static List *get_tables_to_repack(RepackCommand cmd, bool usingindex, + MemoryContext permcxt); +static List *get_tables_to_repack_partitioned(RepackCommand cmd, + MemoryContext cluster_context, + Oid relid, bool rel_is_index); +static bool cluster_is_permitted_for_relation(RepackCommand cmd, + Oid relid, Oid userid); +static Relation process_single_relation(RepackStmt *stmt, + ClusterParams *params); +static Oid determine_clustered_index(Relation rel, bool usingindex, + const char *indexname); +static const char * +RepackCommandAsString(RepackCommand cmd) +{ + switch (cmd) + { + case REPACK_COMMAND_REPACK: + return "REPACK"; + case REPACK_COMMAND_VACUUMFULL: + return "VACUUM"; + case REPACK_COMMAND_CLUSTER: + return "CLUSTER"; + } + return "???"; +} + /*--------------------------------------------------------------------------- * This cluster code allows for clustering multiple tables at once. Because * of this, we cannot just run everything on a single transaction, or we @@ -104,191 +127,155 @@ static bool cluster_is_permitted_for_relation(Oid relid, Oid userid); *--------------------------------------------------------------------------- */ void -cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) +ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel) { - ListCell *lc; ClusterParams params = {0}; - bool verbose = false; Relation rel = NULL; - Oid indexOid = InvalidOid; - MemoryContext cluster_context; + MemoryContext repack_context; List *rtcs; /* Parse option list */ - foreach(lc, stmt->params) + foreach_node(DefElem, opt, stmt->params) { - DefElem *opt = (DefElem *) lfirst(lc); - if (strcmp(opt->defname, "verbose") == 0) - verbose = defGetBoolean(opt); + params.options |= defGetBoolean(opt) ? CLUOPT_VERBOSE : 0; + else if (strcmp(opt->defname, "analyze") == 0 || + strcmp(opt->defname, "analyse") == 0) + params.options |= defGetBoolean(opt) ? CLUOPT_ANALYZE : 0; else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unrecognized CLUSTER option \"%s\"", + errmsg("unrecognized %s option \"%s\"", + RepackCommandAsString(stmt->command), opt->defname), parser_errposition(pstate, opt->location))); } - params.options = (verbose ? CLUOPT_VERBOSE : 0); - + /* + * If a single relation is specified, process it and we're done ... unless + * the relation is a partitioned table, in which case we fall through. + */ if (stmt->relation != NULL) { - /* This is the single-relation case. */ - Oid tableOid; - - /* - * Find, lock, and check permissions on the table. We obtain - * AccessExclusiveLock right away to avoid lock-upgrade hazard in the - * single-transaction case. - */ - tableOid = RangeVarGetRelidExtended(stmt->relation, - AccessExclusiveLock, - 0, - RangeVarCallbackMaintainsTable, - NULL); - rel = table_open(tableOid, NoLock); - - /* - * Reject clustering a remote temp table ... their local buffer - * manager is not going to cope. - */ - if (RELATION_IS_OTHER_TEMP(rel)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot cluster temporary tables of other sessions"))); - - if (stmt->indexname == NULL) - { - ListCell *index; - - /* We need to find the index that has indisclustered set. */ - foreach(index, RelationGetIndexList(rel)) - { - indexOid = lfirst_oid(index); - if (get_index_isclustered(indexOid)) - break; - indexOid = InvalidOid; - } - - if (!OidIsValid(indexOid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("there is no previously clustered index for table \"%s\"", - stmt->relation->relname))); - } - else - { - /* - * The index is expected to be in the same namespace as the - * relation. - */ - indexOid = get_relname_relid(stmt->indexname, - rel->rd_rel->relnamespace); - if (!OidIsValid(indexOid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("index \"%s\" for table \"%s\" does not exist", - stmt->indexname, stmt->relation->relname))); - } - - /* For non-partitioned tables, do what we came here to do. */ - if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) - { - cluster_rel(rel, indexOid, ¶ms); - /* cluster_rel closes the relation, but keeps lock */ - + rel = process_single_relation(stmt, ¶ms); + if (rel == NULL) return; - } } + /* Don't allow this for now. Maybe we can add support for this later */ + if (params.options & CLUOPT_ANALYZE) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot ANALYZE multiple tables")); + /* * By here, we know we are in a multi-table situation. In order to avoid * holding locks for too long, we want to process each table in its own * transaction. This forces us to disallow running inside a user * transaction block. */ - PreventInTransactionBlock(isTopLevel, "CLUSTER"); + PreventInTransactionBlock(isTopLevel, RepackCommandAsString(stmt->command)); /* Also, we need a memory context to hold our list of relations */ - cluster_context = AllocSetContextCreate(PortalContext, - "Cluster", - ALLOCSET_DEFAULT_SIZES); + repack_context = AllocSetContextCreate(PortalContext, + "Repack", + ALLOCSET_DEFAULT_SIZES); + + params.options |= CLUOPT_RECHECK; /* - * Either we're processing a partitioned table, or we were not given any - * table name at all. In either case, obtain a list of relations to - * process. - * - * In the former case, an index name must have been given, so we don't - * need to recheck its "indisclustered" bit, but we have to check that it - * is an index that we can cluster on. In the latter case, we set the - * option bit to have indisclustered verified. - * - * Rechecking the relation itself is necessary here in all cases. + * If we don't have a relation yet, determine a relation list. If we do, + * then it must be a partitioned table, and we want to process its + * partitions. */ - params.options |= CLUOPT_RECHECK; - if (rel != NULL) + if (rel == NULL) { - Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - check_index_is_clusterable(rel, indexOid, AccessShareLock); - rtcs = get_tables_to_cluster_partitioned(cluster_context, indexOid); - - /* close relation, releasing lock on parent table */ - table_close(rel, AccessExclusiveLock); + Assert(stmt->indexname == NULL); + rtcs = get_tables_to_repack(stmt->command, stmt->usingindex, + repack_context); } else { - rtcs = get_tables_to_cluster(cluster_context); - params.options |= CLUOPT_RECHECK_ISCLUSTERED; + Oid relid; + bool rel_is_index; + + Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); + + /* + * If an index name was specified, resolve it now and pass it down. + */ + if (stmt->usingindex) + { + /* + * XXX how should this behave? Passing no index to a partitioned + * table could be useful to have certain partitions clustered by + * some index, and other partitions by a different index. + */ + if (!stmt->indexname) + ereport(ERROR, + errmsg("there is no previously clustered index for table \"%s\"", + RelationGetRelationName(rel))); + + relid = determine_clustered_index(rel, true, stmt->indexname); + if (!OidIsValid(relid)) + elog(ERROR, "unable to determine index to cluster on"); + /* XXX is this the right place for this check? */ + check_index_is_clusterable(rel, relid, AccessExclusiveLock); + rel_is_index = true; + } + else + { + relid = RelationGetRelid(rel); + rel_is_index = false; + } + + rtcs = get_tables_to_repack_partitioned(stmt->command, repack_context, + relid, rel_is_index); + + /* close parent relation, releasing lock on it */ + table_close(rel, AccessExclusiveLock); + rel = NULL; } - /* Do the job. */ - cluster_multiple_rels(rtcs, ¶ms); - - /* Start a new transaction for the cleanup work. */ - StartTransactionCommand(); - - /* Clean up working storage */ - MemoryContextDelete(cluster_context); -} - -/* - * Given a list of relations to cluster, process each of them in a separate - * transaction. - * - * We expect to be in a transaction at start, but there isn't one when we - * return. - */ -static void -cluster_multiple_rels(List *rtcs, ClusterParams *params) -{ - ListCell *lc; - /* Commit to get out of starting transaction */ PopActiveSnapshot(); CommitTransactionCommand(); /* Cluster the tables, each in a separate transaction */ - foreach(lc, rtcs) + Assert(rel == NULL); + foreach_ptr(RelToCluster, rtc, rtcs) { - RelToCluster *rtc = (RelToCluster *) lfirst(lc); - Relation rel; - /* Start a new transaction for each relation. */ StartTransactionCommand(); + /* + * Open the target table, coping with the case where it has been + * dropped. + */ + rel = try_table_open(rtc->tableOid, AccessExclusiveLock); + if (rel == NULL) + { + CommitTransactionCommand(); + continue; + } + /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); - rel = table_open(rtc->tableOid, AccessExclusiveLock); - /* Process this table */ - cluster_rel(rel, rtc->indexOid, params); + cluster_rel(stmt->command, stmt->usingindex, + rel, rtc->indexOid, ¶ms); /* cluster_rel closes the relation, but keeps lock */ PopActiveSnapshot(); CommitTransactionCommand(); } + + /* Start a new transaction for the cleanup work. */ + StartTransactionCommand(); + + /* Clean up working storage */ + MemoryContextDelete(repack_context); } /* @@ -304,11 +291,14 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params) * them incrementally while we load the table. * * If indexOid is InvalidOid, the table will be rewritten in physical order - * instead of index order. This is the new implementation of VACUUM FULL, - * and error messages should refer to the operation as VACUUM not CLUSTER. + * instead of index order. + * + * 'cmd' indicates which command is being executed, to be used for error + * messages. */ void -cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params) +cluster_rel(RepackCommand cmd, bool usingindex, + Relation OldHeap, Oid indexOid, ClusterParams *params) { Oid tableOid = RelationGetRelid(OldHeap); Oid save_userid; @@ -323,13 +313,25 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params) /* Check for user-requested abort. */ CHECK_FOR_INTERRUPTS(); - pgstat_progress_start_command(PROGRESS_COMMAND_CLUSTER, tableOid); - if (OidIsValid(indexOid)) - pgstat_progress_update_param(PROGRESS_CLUSTER_COMMAND, - PROGRESS_CLUSTER_COMMAND_CLUSTER); + if (cmd == REPACK_COMMAND_REPACK) + pgstat_progress_start_command(PROGRESS_COMMAND_REPACK, tableOid); else - pgstat_progress_update_param(PROGRESS_CLUSTER_COMMAND, + pgstat_progress_start_command(PROGRESS_COMMAND_CLUSTER, tableOid); + + if (cmd == REPACK_COMMAND_REPACK) + pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, + PROGRESS_REPACK_COMMAND_REPACK); + else if (cmd == REPACK_COMMAND_CLUSTER) + { + pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, + PROGRESS_CLUSTER_COMMAND_CLUSTER); + } + else + { + Assert(cmd == REPACK_COMMAND_VACUUMFULL); + pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, PROGRESS_CLUSTER_COMMAND_VACUUM_FULL); + } /* * Switch to the table owner's userid, so that any index functions are run @@ -351,63 +353,21 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params) * to cluster a not-previously-clustered index. */ if (recheck) - { - /* Check that the user still has privileges for the relation */ - if (!cluster_is_permitted_for_relation(tableOid, save_userid)) - { - relation_close(OldHeap, AccessExclusiveLock); + if (!cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + params->options)) goto out; - } - - /* - * Silently skip a temp table for a remote session. Only doing this - * check in the "recheck" case is appropriate (which currently means - * somebody is executing a database-wide CLUSTER or on a partitioned - * table), because there is another check in cluster() which will stop - * any attempt to cluster remote temp tables by name. There is - * another check in cluster_rel which is redundant, but we leave it - * for extra safety. - */ - if (RELATION_IS_OTHER_TEMP(OldHeap)) - { - relation_close(OldHeap, AccessExclusiveLock); - goto out; - } - - if (OidIsValid(indexOid)) - { - /* - * Check that the index still exists - */ - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid))) - { - relation_close(OldHeap, AccessExclusiveLock); - goto out; - } - - /* - * Check that the index is still the one with indisclustered set, - * if needed. - */ - if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) != 0 && - !get_index_isclustered(indexOid)) - { - relation_close(OldHeap, AccessExclusiveLock); - goto out; - } - } - } /* - * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER - * would work in most respects, but the index would only get marked as - * indisclustered in the current database, leading to unexpected behavior - * if CLUSTER were later invoked in another database. + * We allow repacking shared catalogs only when not using an index. It + * would work to use an index in most respects, but the index would only + * get marked as indisclustered in the current database, leading to + * unexpected behavior if CLUSTER were later invoked in another database. */ - if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared) + if (usingindex && OldHeap->rd_rel->relisshared) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot cluster a shared catalog"))); + errmsg("cannot run \"%s\" on a shared catalog", + RepackCommandAsString(cmd)))); /* * Don't process temp tables of other backends ... their local buffer @@ -415,21 +375,30 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params) */ if (RELATION_IS_OTHER_TEMP(OldHeap)) { - if (OidIsValid(indexOid)) + if (cmd == REPACK_COMMAND_CLUSTER) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot cluster temporary tables of other sessions"))); + else if (cmd == REPACK_COMMAND_REPACK) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot repack temporary tables of other sessions"))); + } else + { + Assert(cmd == REPACK_COMMAND_VACUUMFULL); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot vacuum temporary tables of other sessions"))); + } } /* * Also check for active uses of the relation in the current transaction, * including open scans and pending AFTER trigger events. */ - CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM"); + CheckTableNotInUse(OldHeap, RepackCommandAsString(cmd)); /* Check heap and index are valid to cluster on */ if (OidIsValid(indexOid)) @@ -469,7 +438,7 @@ cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params) TransferPredicateLocksToHeapRelation(OldHeap); /* rebuild_relation does all the dirty work */ - rebuild_relation(OldHeap, index, verbose); + rebuild_relation(cmd, usingindex, OldHeap, index, verbose); /* rebuild_relation closes OldHeap, and index if valid */ out: @@ -482,6 +451,63 @@ out: pgstat_progress_end_command(); } +/* + * Check if the table (and its index) still meets the requirements of + * cluster_rel(). + */ +static bool +cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid, + Oid userid, int options) +{ + Oid tableOid = RelationGetRelid(OldHeap); + + /* Check that the user still has privileges for the relation */ + if (!cluster_is_permitted_for_relation(cmd, tableOid, userid)) + { + relation_close(OldHeap, AccessExclusiveLock); + return false; + } + + /* + * Silently skip a temp table for a remote session. Only doing this check + * in the "recheck" case is appropriate (which currently means somebody is + * executing a database-wide CLUSTER or on a partitioned table), because + * there is another check in cluster() which will stop any attempt to + * cluster remote temp tables by name. There is another check in + * cluster_rel which is redundant, but we leave it for extra safety. + */ + if (RELATION_IS_OTHER_TEMP(OldHeap)) + { + relation_close(OldHeap, AccessExclusiveLock); + return false; + } + + if (OidIsValid(indexOid)) + { + /* + * Check that the index still exists + */ + if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid))) + { + relation_close(OldHeap, AccessExclusiveLock); + return false; + } + + /* + * Check that the index is still the one with indisclustered set, if + * needed. + */ + if ((options & CLUOPT_RECHECK_ISCLUSTERED) != 0 && + !get_index_isclustered(indexOid)) + { + relation_close(OldHeap, AccessExclusiveLock); + return false; + } + } + + return true; +} + /* * Verify that the specified heap and index are valid to cluster on * @@ -626,7 +652,8 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) * On exit, they are closed, but locks on them are not released. */ static void -rebuild_relation(Relation OldHeap, Relation index, bool verbose) +rebuild_relation(RepackCommand cmd, bool usingindex, + Relation OldHeap, Relation index, bool verbose) { Oid tableOid = RelationGetRelid(OldHeap); Oid accessMethod = OldHeap->rd_rel->relam; @@ -642,8 +669,8 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose) Assert(CheckRelationLockedByMe(OldHeap, AccessExclusiveLock, false) && (index == NULL || CheckRelationLockedByMe(index, AccessExclusiveLock, false))); - if (index) - /* Mark the correct index as clustered */ + /* for CLUSTER or REPACK USING INDEX, mark the index as the one to use */ + if (usingindex) mark_index_clustered(OldHeap, RelationGetRelid(index), true); /* Remember info about rel before closing OldHeap */ @@ -1458,8 +1485,8 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, int i; /* Report that we are now swapping relation files */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_SWAP_REL_FILES); /* Zero out possible results from swapped_relation_files */ memset(mapped_tables, 0, sizeof(mapped_tables)); @@ -1509,14 +1536,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT; /* Report that we are now reindexing relations */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_REBUILD_INDEX); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_REBUILD_INDEX); reindex_relation(NULL, OIDOldHeap, reindex_flags, &reindex_params); /* Report that we are now doing clean up */ - pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, - PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP); + pgstat_progress_update_param(PROGRESS_REPACK_PHASE, + PROGRESS_REPACK_PHASE_FINAL_CLEANUP); /* * If the relation being rebuilt is pg_class, swap_relation_files() @@ -1632,69 +1659,137 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, } } - /* - * Get a list of tables that the current user has privileges on and - * have indisclustered set. Return the list in a List * of RelToCluster - * (stored in the specified memory context), each one giving the tableOid - * and the indexOid on which the table is already clustered. + * Determine which relations to process, when REPACK/CLUSTER is called + * without specifying a table name. The exact process depends on whether + * USING INDEX was given or not, and in any case we only return tables and + * materialized views that the current user has privileges to repack/cluster. + * + * If USING INDEX was given, we scan pg_index to find those that have + * indisclustered set; if it was not given, scan pg_class and return all + * tables. + * + * Return it as a list of RelToCluster in the given memory context. */ static List * -get_tables_to_cluster(MemoryContext cluster_context) +get_tables_to_repack(RepackCommand command, bool usingindex, + MemoryContext permcxt) { - Relation indRelation; + Relation catalog; TableScanDesc scan; - ScanKeyData entry; - HeapTuple indexTuple; - Form_pg_index index; + HeapTuple tuple; MemoryContext old_context; List *rtcs = NIL; - /* - * Get all indexes that have indisclustered set and that the current user - * has the appropriate privileges for. - */ - indRelation = table_open(IndexRelationId, AccessShareLock); - ScanKeyInit(&entry, - Anum_pg_index_indisclustered, - BTEqualStrategyNumber, F_BOOLEQ, - BoolGetDatum(true)); - scan = table_beginscan_catalog(indRelation, 1, &entry); - while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + if (usingindex) { - RelToCluster *rtc; + ScanKeyData entry; - index = (Form_pg_index) GETSTRUCT(indexTuple); + catalog = table_open(IndexRelationId, AccessShareLock); + ScanKeyInit(&entry, + Anum_pg_index_indisclustered, + BTEqualStrategyNumber, F_BOOLEQ, + BoolGetDatum(true)); + scan = table_beginscan_catalog(catalog, 1, &entry); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + RelToCluster *rtc; + Form_pg_index index; - if (!cluster_is_permitted_for_relation(index->indrelid, GetUserId())) - continue; + index = (Form_pg_index) GETSTRUCT(tuple); - /* Use a permanent memory context for the result list */ - old_context = MemoryContextSwitchTo(cluster_context); + /* + * XXX I think the only reason there's no test failure here is + * that we seldom have clustered indexes that would be affected by + * concurrency. Maybe we should also do the + * ConditionalLockRelationOid+SearchSysCacheExists dance that we + * do below. + */ + if (!cluster_is_permitted_for_relation(command, index->indrelid, + GetUserId())) + continue; - rtc = (RelToCluster *) palloc(sizeof(RelToCluster)); - rtc->tableOid = index->indrelid; - rtc->indexOid = index->indexrelid; - rtcs = lappend(rtcs, rtc); + /* Use a permanent memory context for the result list */ + old_context = MemoryContextSwitchTo(permcxt); - MemoryContextSwitchTo(old_context); + rtc = (RelToCluster *) palloc(sizeof(RelToCluster)); + rtc->tableOid = index->indrelid; + rtc->indexOid = index->indexrelid; + rtcs = lappend(rtcs, rtc); + + MemoryContextSwitchTo(old_context); + } } - table_endscan(scan); + else + { + catalog = table_open(RelationRelationId, AccessShareLock); + scan = table_beginscan_catalog(catalog, 0, NULL); - relation_close(indRelation, AccessShareLock); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + RelToCluster *rtc; + Form_pg_class class; + + class = (Form_pg_class) GETSTRUCT(tuple); + + /* + * Try to obtain a light lock on the table, to ensure it doesn't + * go away while we collect the list. If we cannot, just + * disregard the table. XXX we could release at the bottom of the + * loop, but for now just hold it until this transaction is + * finished. + */ + if (!ConditionalLockRelationOid(class->oid, AccessShareLock)) + continue; + + /* Verify that the table still exists. */ + if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid))) + { + /* Release useless lock */ + UnlockRelationOid(class->oid, AccessShareLock); + continue; + } + + /* Can only process plain tables and matviews */ + if (class->relkind != RELKIND_RELATION && + class->relkind != RELKIND_MATVIEW) + continue; + + if (!cluster_is_permitted_for_relation(command, class->oid, + GetUserId())) + continue; + + /* Use a permanent memory context for the result list */ + old_context = MemoryContextSwitchTo(permcxt); + + rtc = (RelToCluster *) palloc(sizeof(RelToCluster)); + rtc->tableOid = class->oid; + rtc->indexOid = InvalidOid; + rtcs = lappend(rtcs, rtc); + + MemoryContextSwitchTo(old_context); + } + } + + table_endscan(scan); + relation_close(catalog, AccessShareLock); return rtcs; } /* - * Given an index on a partitioned table, return a list of RelToCluster for + * Given a partitioned table or its index, return a list of RelToCluster for * all the children leaves tables/indexes. * * Like expand_vacuum_rel, but here caller must hold AccessExclusiveLock * on the table containing the index. + * + * 'rel_is_index' tells whether 'relid' is that of an index (true) or of the + * owning relation. */ static List * -get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) +get_tables_to_repack_partitioned(RepackCommand cmd, MemoryContext cluster_context, + Oid relid, bool rel_is_index) { List *inhoids; ListCell *lc; @@ -1702,17 +1797,33 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) MemoryContext old_context; /* Do not lock the children until they're processed */ - inhoids = find_all_inheritors(indexOid, NoLock, NULL); + inhoids = find_all_inheritors(relid, NoLock, NULL); foreach(lc, inhoids) { - Oid indexrelid = lfirst_oid(lc); - Oid relid = IndexGetRelation(indexrelid, false); + Oid inhoid = lfirst_oid(lc); + Oid inhrelid, + inhindid; RelToCluster *rtc; - /* consider only leaf indexes */ - if (get_rel_relkind(indexrelid) != RELKIND_INDEX) - continue; + if (rel_is_index) + { + /* consider only leaf indexes */ + if (get_rel_relkind(inhoid) != RELKIND_INDEX) + continue; + + inhrelid = IndexGetRelation(inhoid, false); + inhindid = inhoid; + } + else + { + /* consider only leaf relations */ + if (get_rel_relkind(inhoid) != RELKIND_RELATION) + continue; + + inhrelid = inhoid; + inhindid = InvalidOid; + } /* * It's possible that the user does not have privileges to CLUSTER the @@ -1720,15 +1831,15 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) * table. We skip any partitions which the user is not permitted to * CLUSTER. */ - if (!cluster_is_permitted_for_relation(relid, GetUserId())) + if (!cluster_is_permitted_for_relation(cmd, inhrelid, GetUserId())) continue; /* Use a permanent memory context for the result list */ old_context = MemoryContextSwitchTo(cluster_context); rtc = (RelToCluster *) palloc(sizeof(RelToCluster)); - rtc->tableOid = relid; - rtc->indexOid = indexrelid; + rtc->tableOid = inhrelid; + rtc->indexOid = inhindid; rtcs = lappend(rtcs, rtc); MemoryContextSwitchTo(old_context); @@ -1742,13 +1853,148 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) * function emits a WARNING. */ static bool -cluster_is_permitted_for_relation(Oid relid, Oid userid) +cluster_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid) { if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK) return true; + Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK); ereport(WARNING, - (errmsg("permission denied to cluster \"%s\", skipping it", - get_rel_name(relid)))); + errmsg("permission denied to execute %s on \"%s\", skipping it", + cmd == REPACK_COMMAND_CLUSTER ? "CLUSTER" : "REPACK", + get_rel_name(relid))); + return false; } + + +/* + * Given a RepackStmt with an indicated relation name, resolve the relation + * name, obtain lock on it, then determine what to do based on the relation + * type: if it's not a partitioned table, repack it as indicated (using an + * existing clustered index, or following the indicated index), and return + * NULL. + * + * On the other hand, if the table is partitioned, do nothing further and + * instead return the opened relcache entry, so that caller can process the + * partitions using the multiple-table handling code. The index name is not + * resolve in this case. + */ +static Relation +process_single_relation(RepackStmt *stmt, ClusterParams *params) +{ + Relation rel; + Oid tableOid; + + Assert(stmt->relation != NULL); + Assert(stmt->command == REPACK_COMMAND_CLUSTER || + stmt->command == REPACK_COMMAND_REPACK); + + /* + * Find, lock, and check permissions on the table. We obtain + * AccessExclusiveLock right away to avoid lock-upgrade hazard in the + * single-transaction case. + */ + tableOid = RangeVarGetRelidExtended(stmt->relation, + AccessExclusiveLock, + 0, + RangeVarCallbackMaintainsTable, + NULL); + rel = table_open(tableOid, NoLock); + + /* + * Reject clustering a remote temp table ... their local buffer manager is + * not going to cope. + */ + if (RELATION_IS_OTHER_TEMP(rel)) + { + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot execute %s on temporary tables of other sessions", + RepackCommandAsString(stmt->command))); + } + + /* + * For partitioned tables, let caller handle this. Otherwise, process it + * here and we're done. + */ + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + return rel; + else + { + Oid indexOid; + + indexOid = determine_clustered_index(rel, stmt->usingindex, + stmt->indexname); + if (OidIsValid(indexOid)) + check_index_is_clusterable(rel, indexOid, AccessExclusiveLock); + cluster_rel(stmt->command, stmt->usingindex, rel, indexOid, params); + + /* Do an analyze, if requested */ + if (params->options & CLUOPT_ANALYZE) + { + VacuumParams vac_params = {0}; + + vac_params.options |= VACOPT_ANALYZE; + if (params->options & CLUOPT_VERBOSE) + vac_params.options |= VACOPT_VERBOSE; + analyze_rel(RelationGetRelid(rel), NULL, vac_params, NIL, true, + NULL); + } + + return NULL; + } +} + +/* + * Given a relation and the usingindex/indexname options in a + * REPACK USING INDEX or CLUSTER command, return the OID of the index to use + * for clustering the table. + * + * Caller must hold lock on the relation so that the set of indexes doesn't + * change, and must call check_index_is_clusterable. + */ +static Oid +determine_clustered_index(Relation rel, bool usingindex, const char *indexname) +{ + Oid indexOid; + + if (indexname == NULL && usingindex) + { + ListCell *lc; + + /* Find an index with indisclustered set, or report error */ + foreach(lc, RelationGetIndexList(rel)) + { + indexOid = lfirst_oid(lc); + + if (get_index_isclustered(indexOid)) + break; + indexOid = InvalidOid; + } + + if (!OidIsValid(indexOid)) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("there is no previously clustered index for table \"%s\"", + RelationGetRelationName(rel))); + } + else if (indexname != NULL) + { + /* + * An index was specified; figure out its OID. It must be in the same + * namespace as the relation. + */ + indexOid = get_relname_relid(indexname, + rel->rd_rel->relnamespace); + if (!OidIsValid(indexOid)) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("index \"%s\" for table \"%s\" does not exist", + indexname, RelationGetRelationName(rel))); + } + else + indexOid = InvalidOid; + + return indexOid; +} diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 733ef40ae7c..8863ad0e8bd 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -2287,7 +2287,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, cluster_params.options |= CLUOPT_VERBOSE; /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ - cluster_rel(rel, InvalidOid, &cluster_params); + cluster_rel(REPACK_COMMAND_VACUUMFULL, false, rel, InvalidOid, + &cluster_params); /* cluster_rel closes the relation, but keeps lock */ rel = NULL; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9fd48acb1f8..ab52c171b84 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -280,7 +280,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); AlterCompositeTypeStmt AlterUserMappingStmt AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt AlterStatsStmt AlterDefaultPrivilegesStmt DefACLAction - AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt + AnalyzeStmt CallStmt ClosePortalStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt @@ -297,7 +297,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt MergeStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt - RemoveFuncStmt RemoveOperStmt RenameStmt ReturnStmt RevokeStmt RevokeRoleStmt + RemoveFuncStmt RemoveOperStmt RenameStmt RepackStmt ReturnStmt RevokeStmt RevokeRoleStmt RuleActionStmt RuleActionStmtOrEmpty RuleStmt SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy TruncateStmt UnlistenStmt UpdateStmt VacuumStmt @@ -316,7 +316,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <str> opt_single_name %type <list> opt_qualified_name -%type <boolean> opt_concurrently +%type <boolean> opt_concurrently opt_usingindex %type <dbehavior> opt_drop_behavior %type <list> opt_utility_option_list %type <list> utility_option_list @@ -763,7 +763,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); QUOTE QUOTES RANGE READ REAL REASSIGN RECURSIVE REF_P REFERENCES REFERENCING - REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA + REFRESH REINDEX RELATIVE_P RELEASE RENAME REPACK REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROUTINES ROW ROWS RULE @@ -1025,7 +1025,6 @@ stmt: | CallStmt | CheckPointStmt | ClosePortalStmt - | ClusterStmt | CommentStmt | ConstraintsSetStmt | CopyStmt @@ -1099,6 +1098,7 @@ stmt: | RemoveFuncStmt | RemoveOperStmt | RenameStmt + | RepackStmt | RevokeStmt | RevokeRoleStmt | RuleStmt @@ -1135,6 +1135,11 @@ opt_concurrently: | /*EMPTY*/ { $$ = false; } ; +opt_usingindex: + USING INDEX { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + opt_drop_behavior: CASCADE { $$ = DROP_CASCADE; } | RESTRICT { $$ = DROP_RESTRICT; } @@ -11913,38 +11918,91 @@ CreateConversionStmt: /***************************************************************************** * * QUERY: + * REPACK [ (options) ] [ <qualified_name> [ USING INDEX <index_name> ] ] + * + * obsolete variants: * CLUSTER (options) [ <qualified_name> [ USING <index_name> ] ] * CLUSTER [VERBOSE] [ <qualified_name> [ USING <index_name> ] ] * CLUSTER [VERBOSE] <index_name> ON <qualified_name> (for pre-8.3) * *****************************************************************************/ -ClusterStmt: - CLUSTER '(' utility_option_list ')' qualified_name cluster_index_specification +RepackStmt: + REPACK opt_utility_option_list qualified_name USING INDEX name { - ClusterStmt *n = makeNode(ClusterStmt); + RepackStmt *n = makeNode(RepackStmt); + n->command = REPACK_COMMAND_REPACK; + n->relation = $3; + n->indexname = $6; + n->usingindex = true; + n->params = $2; + $$ = (Node *) n; + } + | REPACK opt_utility_option_list qualified_name opt_usingindex + { + RepackStmt *n = makeNode(RepackStmt); + + n->command = REPACK_COMMAND_REPACK; + n->relation = $3; + n->indexname = NULL; + n->usingindex = $4; + n->params = $2; + $$ = (Node *) n; + } + | REPACK '(' utility_option_list ')' + { + RepackStmt *n = makeNode(RepackStmt); + + n->command = REPACK_COMMAND_REPACK; + n->relation = NULL; + n->indexname = NULL; + n->usingindex = false; + n->params = $3; + $$ = (Node *) n; + } + | REPACK opt_usingindex + { + RepackStmt *n = makeNode(RepackStmt); + + n->command = REPACK_COMMAND_REPACK; + n->relation = NULL; + n->indexname = NULL; + n->usingindex = $2; + n->params = NIL; + $$ = (Node *) n; + } + | CLUSTER '(' utility_option_list ')' qualified_name cluster_index_specification + { + RepackStmt *n = makeNode(RepackStmt); + + n->command = REPACK_COMMAND_CLUSTER; n->relation = $5; n->indexname = $6; + n->usingindex = true; n->params = $3; $$ = (Node *) n; } | CLUSTER opt_utility_option_list { - ClusterStmt *n = makeNode(ClusterStmt); + RepackStmt *n = makeNode(RepackStmt); + n->command = REPACK_COMMAND_CLUSTER; n->relation = NULL; n->indexname = NULL; + n->usingindex = true; n->params = $2; $$ = (Node *) n; } /* unparenthesized VERBOSE kept for pre-14 compatibility */ | CLUSTER opt_verbose qualified_name cluster_index_specification { - ClusterStmt *n = makeNode(ClusterStmt); + RepackStmt *n = makeNode(RepackStmt); + n->command = REPACK_COMMAND_CLUSTER; n->relation = $3; n->indexname = $4; + n->usingindex = true; if ($2) n->params = list_make1(makeDefElem("verbose", NULL, @2)); $$ = (Node *) n; @@ -11952,20 +12010,24 @@ ClusterStmt: /* unparenthesized VERBOSE kept for pre-17 compatibility */ | CLUSTER VERBOSE { - ClusterStmt *n = makeNode(ClusterStmt); + RepackStmt *n = makeNode(RepackStmt); + n->command = REPACK_COMMAND_CLUSTER; n->relation = NULL; n->indexname = NULL; + n->usingindex = true; n->params = list_make1(makeDefElem("verbose", NULL, @2)); $$ = (Node *) n; } /* kept for pre-8.3 compatibility */ | CLUSTER opt_verbose name ON qualified_name { - ClusterStmt *n = makeNode(ClusterStmt); + RepackStmt *n = makeNode(RepackStmt); + n->command = REPACK_COMMAND_CLUSTER; n->relation = $5; n->indexname = $3; + n->usingindex = true; if ($2) n->params = list_make1(makeDefElem("verbose", NULL, @2)); $$ = (Node *) n; @@ -17961,6 +18023,7 @@ unreserved_keyword: | RELATIVE_P | RELEASE | RENAME + | REPACK | REPEATABLE | REPLACE | REPLICA @@ -18593,6 +18656,7 @@ bare_label_keyword: | RELATIVE_P | RELEASE | RENAME + | REPACK | REPEATABLE | REPLACE | REPLICA diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 918db53dd5e..1295dc25d02 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -277,9 +277,9 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree) return COMMAND_OK_IN_RECOVERY | COMMAND_OK_IN_READ_ONLY_TXN; } - case T_ClusterStmt: case T_ReindexStmt: case T_VacuumStmt: + case T_RepackStmt: { /* * These commands write WAL, so they're not strictly @@ -854,14 +854,14 @@ standard_ProcessUtility(PlannedStmt *pstmt, ExecuteCallStmt(castNode(CallStmt, parsetree), params, isAtomicContext, dest); break; - case T_ClusterStmt: - cluster(pstate, (ClusterStmt *) parsetree, isTopLevel); - break; - case T_VacuumStmt: ExecVacuum(pstate, (VacuumStmt *) parsetree, isTopLevel); break; + case T_RepackStmt: + ExecRepack(pstate, (RepackStmt *) parsetree, isTopLevel); + break; + case T_ExplainStmt: ExplainQuery(pstate, (ExplainStmt *) parsetree, params, dest); break; @@ -2850,10 +2850,6 @@ CreateCommandTag(Node *parsetree) tag = CMDTAG_CALL; break; - case T_ClusterStmt: - tag = CMDTAG_CLUSTER; - break; - case T_VacuumStmt: if (((VacuumStmt *) parsetree)->is_vacuumcmd) tag = CMDTAG_VACUUM; @@ -2861,6 +2857,10 @@ CreateCommandTag(Node *parsetree) tag = CMDTAG_ANALYZE; break; + case T_RepackStmt: + tag = CMDTAG_REPACK; + break; + case T_ExplainStmt: tag = CMDTAG_EXPLAIN; break; @@ -3498,7 +3498,7 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_ALL; break; - case T_ClusterStmt: + case T_RepackStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index c756c2bebaa..a1e10e8c2f6 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -268,6 +268,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) cmdtype = PROGRESS_COMMAND_ANALYZE; else if (pg_strcasecmp(cmd, "CLUSTER") == 0) cmdtype = PROGRESS_COMMAND_CLUSTER; + else if (pg_strcasecmp(cmd, "REPACK") == 0) + cmdtype = PROGRESS_COMMAND_REPACK; else if (pg_strcasecmp(cmd, "CREATE INDEX") == 0) cmdtype = PROGRESS_COMMAND_CREATE_INDEX; else if (pg_strcasecmp(cmd, "BASEBACKUP") == 0) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 6b20a4404b2..df6c82e6d7b 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1247,7 +1247,7 @@ static const char *const sql_commands[] = { "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK", "MERGE INTO", "MOVE", "NOTIFY", "PREPARE", - "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE", + "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE", "REPACK", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START", "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH", @@ -4999,6 +4999,37 @@ match_previous_words(int pattern_id, COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); } +/* REPACK */ + else if (Matches("REPACK")) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables); + else if (Matches("REPACK", "(*)")) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables); + /* If we have REPACK <sth>, then add "USING INDEX" */ + else if (Matches("REPACK", MatchAnyExcept("("))) + COMPLETE_WITH("USING INDEX"); + /* If we have REPACK (*) <sth>, then add "USING INDEX" */ + else if (Matches("REPACK", "(*)", MatchAny)) + COMPLETE_WITH("USING INDEX"); + /* If we have REPACK <sth> USING, then add the index as well */ + else if (Matches("REPACK", MatchAny, "USING", "INDEX")) + { + set_completion_reference(prev3_wd); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table); + } + else if (HeadMatches("REPACK", "(*") && + !HeadMatches("REPACK", "(*)")) + { + /* + * This fires if we're in an unfinished parenthesized option list. + * get_previous_words treats a completed parenthesized option list as + * one word, so the above test is correct. + */ + if (ends_with(prev_wd, '(') || ends_with(prev_wd, ',')) + COMPLETE_WITH("VERBOSE"); + else if (TailMatches("VERBOSE")) + COMPLETE_WITH("ON", "OFF"); + } + /* SECURITY LABEL */ else if (Matches("SECURITY")) COMPLETE_WITH("LABEL"); diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index 019ca06455d..f0c1bd4175c 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -16,7 +16,7 @@ subdir = src/bin/scripts top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -PROGRAMS = createdb createuser dropdb dropuser clusterdb vacuumdb reindexdb pg_isready +PROGRAMS = createdb createuser dropdb dropuser clusterdb vacuumdb reindexdb pg_isready pg_repackdb override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) @@ -31,6 +31,7 @@ clusterdb: clusterdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport su vacuumdb: vacuumdb.o vacuuming.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils +pg_repackdb: pg_repackdb.o vacuuming.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs $(INSTALL_PROGRAM) createdb$(X) '$(DESTDIR)$(bindir)'/createdb$(X) @@ -41,6 +42,7 @@ install: all installdirs $(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(bindir)'/vacuumdb$(X) $(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(bindir)'/reindexdb$(X) $(INSTALL_PROGRAM) pg_isready$(X) '$(DESTDIR)$(bindir)'/pg_isready$(X) + $(INSTALL_PROGRAM) pg_repackdb$(X) '$(DESTDIR)$(bindir)'/pg_repackdb$(X) installdirs: $(MKDIR_P) '$(DESTDIR)$(bindir)' diff --git a/src/bin/scripts/meson.build b/src/bin/scripts/meson.build index a4fed59d1c9..be573cae682 100644 --- a/src/bin/scripts/meson.build +++ b/src/bin/scripts/meson.build @@ -42,6 +42,7 @@ vacuuming_common = static_library('libvacuuming_common', binaries = [ 'vacuumdb', + 'pg_repackdb', ] foreach binary : binaries binary_sources = files('@0@.c'.format(binary)) @@ -80,6 +81,7 @@ tests += { 't/100_vacuumdb.pl', 't/101_vacuumdb_all.pl', 't/102_vacuumdb_stages.pl', + 't/103_repackdb.pl', 't/200_connstr.pl', ], }, diff --git a/src/bin/scripts/pg_repackdb.c b/src/bin/scripts/pg_repackdb.c new file mode 100644 index 00000000000..8b4cfca7fe1 --- /dev/null +++ b/src/bin/scripts/pg_repackdb.c @@ -0,0 +1,238 @@ +/*------------------------------------------------------------------------- + * + * pg_repackdb + * An utility to run REPACK + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * FIXME: this is missing a way to specify the index to use to repack one + * table, or whether to pass a WITH INDEX clause when multiple tables are + * used. Something like --index[=indexname]. Adding that bleeds into + * vacuuming.c as well. + * + * src/bin/scripts/pg_repackdb.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include <limits.h> + +#include "common.h" +#include "common/logging.h" +#include "fe_utils/option_utils.h" +#include "vacuuming.h" + +static void help(const char *progname); +void check_objfilter(VacObjFilter objfilter); + +int +main(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"host", required_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"username", required_argument, NULL, 'U'}, + {"no-password", no_argument, NULL, 'w'}, + {"password", no_argument, NULL, 'W'}, + {"echo", no_argument, NULL, 'e'}, + {"quiet", no_argument, NULL, 'q'}, + {"dbname", required_argument, NULL, 'd'}, + {"all", no_argument, NULL, 'a'}, + /* XXX this could be 'i', but optional_arg is messy */ + {"index", optional_argument, NULL, 1}, + {"table", required_argument, NULL, 't'}, + {"verbose", no_argument, NULL, 'v'}, + {"jobs", required_argument, NULL, 'j'}, + {"schema", required_argument, NULL, 'n'}, + {"exclude-schema", required_argument, NULL, 'N'}, + {"maintenance-db", required_argument, NULL, 2}, + {NULL, 0, NULL, 0} + }; + + const char *progname; + int optindex; + int c; + const char *dbname = NULL; + const char *maintenance_db = NULL; + ConnParams cparams; + bool echo = false; + bool quiet = false; + vacuumingOptions vacopts; + VacObjFilter objfilter = OBJFILTER_NONE; + SimpleStringList objects = {NULL, NULL}; + int concurrentCons = 1; + int tbl_count = 0; + int ret; + + /* initialize options */ + memset(&vacopts, 0, sizeof(vacopts)); + vacopts.mode = MODE_REPACK; + + /* the same for connection parameters */ + memset(&cparams, 0, sizeof(cparams)); + cparams.prompt_password = TRI_DEFAULT; + + pg_logging_init(argv[0]); + progname = get_progname(argv[0]); + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts")); + + handle_help_version_opts(argc, argv, progname, help); + + while ((c = getopt_long(argc, argv, "ad:eh:j:n:N:p:qt:U:vwW", + long_options, &optindex)) != -1) + { + switch (c) + { + case 'a': + objfilter |= OBJFILTER_ALL_DBS; + break; + case 'd': + objfilter |= OBJFILTER_DATABASE; + dbname = pg_strdup(optarg); + break; + case 'e': + echo = true; + break; + case 'h': + cparams.pghost = pg_strdup(optarg); + break; + case 'j': + if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX, + &concurrentCons)) + exit(1); + break; + case 'n': + objfilter |= OBJFILTER_SCHEMA; + simple_string_list_append(&objects, optarg); + break; + case 'N': + objfilter |= OBJFILTER_SCHEMA_EXCLUDE; + simple_string_list_append(&objects, optarg); + break; + case 'p': + cparams.pgport = pg_strdup(optarg); + break; + case 'q': + quiet = true; + break; + case 't': + objfilter |= OBJFILTER_TABLE; + simple_string_list_append(&objects, optarg); + tbl_count++; + break; + case 'U': + cparams.pguser = pg_strdup(optarg); + break; + case 'v': + vacopts.verbose = true; + break; + case 'w': + cparams.prompt_password = TRI_NO; + break; + case 'W': + cparams.prompt_password = TRI_YES; + break; + case 1: + vacopts.using_index = true; + if (optarg) + vacopts.indexname = pg_strdup(optarg); + else + vacopts.indexname = NULL; + break; + case 2: + maintenance_db = pg_strdup(optarg); + break; + default: + /* getopt_long already emitted a complaint */ + pg_log_error_hint("Try \"%s --help\" for more information.", progname); + exit(1); + } + } + + /* + * Non-option argument specifies database name as long as it wasn't + * already specified with -d / --dbname + */ + if (optind < argc && dbname == NULL) + { + objfilter |= OBJFILTER_DATABASE; + dbname = argv[optind]; + optind++; + } + + if (optind < argc) + { + pg_log_error("too many command-line arguments (first is \"%s\")", + argv[optind]); + pg_log_error_hint("Try \"%s --help\" for more information.", progname); + exit(1); + } + + /* + * Validate the combination of filters specified in the command-line + * options. + */ + check_objfilter(objfilter); + + ret = vacuuming_main(&cparams, dbname, maintenance_db, &vacopts, + objfilter, &objects, + tbl_count, false, concurrentCons, + progname, echo, quiet); + exit(ret); +} + +/* + * Verify that the filters used at command line are compatible. + */ +void +check_objfilter(VacObjFilter objfilter) +{ + if ((objfilter & OBJFILTER_ALL_DBS) && + (objfilter & OBJFILTER_DATABASE)) + pg_fatal("cannot repack all databases and a specific one at the same time"); + + if ((objfilter & OBJFILTER_TABLE) && + (objfilter & OBJFILTER_SCHEMA)) + pg_fatal("cannot repack all tables in schema(s) and specific table(s) at the same time"); + + if ((objfilter & OBJFILTER_TABLE) && + (objfilter & OBJFILTER_SCHEMA_EXCLUDE)) + pg_fatal("cannot repack specific table(s) and exclude schema(s) at the same time"); + + if ((objfilter & OBJFILTER_SCHEMA) && + (objfilter & OBJFILTER_SCHEMA_EXCLUDE)) + pg_fatal("cannot repack all tables in schema(s) and exclude schema(s) at the same time"); +} + +static void +help(const char *progname) +{ + printf(_("%s repacks a PostgreSQL database.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION]... [DBNAME]\n"), progname); + printf(_("\nOptions:\n")); + printf(_(" -a, --all repack all databases\n")); + printf(_(" -d, --dbname=DBNAME database to repack\n")); + printf(_(" -e, --echo show the commands being sent to the server\n")); + printf(_(" -j, --jobs=NUM use this many concurrent connections to repack\n")); + printf(_(" -n, --schema=SCHEMA repack tables in the specified schema(s) only\n")); + printf(_(" -N, --exclude-schema=SCHEMA do not repack tables in the specified schema(s)\n")); + printf(_(" -q, --quiet don't write any messages\n")); + printf(_(" -t, --table='TABLE' repack specific table(s) only\n")); + printf(_(" -v, --verbose write a lot of output\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_(" -?, --help show this help, then exit\n")); + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); + printf(_(" -p, --port=PORT database server port\n")); + printf(_(" -U, --username=USERNAME user name to connect as\n")); + printf(_(" -w, --no-password never prompt for password\n")); + printf(_(" -W, --password force password prompt\n")); + printf(_(" --maintenance-db=DBNAME alternate maintenance database\n")); + printf(_("\nRead the description of the SQL command REPACK for details.\n")); + printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +} diff --git a/src/bin/scripts/t/103_repackdb.pl b/src/bin/scripts/t/103_repackdb.pl new file mode 100644 index 00000000000..51de4d7ab34 --- /dev/null +++ b/src/bin/scripts/t/103_repackdb.pl @@ -0,0 +1,24 @@ +# Copyright (c) 2021-2025, PostgreSQL Global Development Group + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +program_help_ok('pg_repackdb'); +program_version_ok('pg_repackdb'); +program_options_handling_ok('pg_repackdb'); + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->start; + +$node->issues_sql_like( + [ 'pg_repackdb', 'postgres' ], + qr/statement: REPACK.*;/, + 'SQL REPACK run'); + + +done_testing(); diff --git a/src/bin/scripts/vacuuming.c b/src/bin/scripts/vacuuming.c index b9f2e507557..f926cf2124a 100644 --- a/src/bin/scripts/vacuuming.c +++ b/src/bin/scripts/vacuuming.c @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * vacuuming.c - * Helper routines for vacuumdb + * Helper routines for vacuumdb and pg_repackdb * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -46,8 +46,8 @@ static SimpleStringList *retrieve_objects(PGconn *conn, bool echo); static void prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, vacuumingOptions *vacopts, const char *table); -static void run_vacuum_command(PGconn *conn, const char *sql, bool echo, - const char *table); +static void run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts, + const char *sql, bool echo, const char *table); /* * Executes vacuum/analyze as indicated, or dies in case of failure. @@ -192,6 +192,14 @@ vacuum_one_database(ConnParams *cparams, conn = connectDatabase(cparams, progname, echo, false, true); + if (vacopts->mode == MODE_REPACK && PQserverVersion(conn) < 190000) + { + /* XXX arguably, here we should use VACUUM FULL instead of failing */ + PQfinish(conn); + pg_fatal("cannot use the \"%s\" command on server versions older than PostgreSQL %s", + "REPACK", "19"); + } + if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600) { PQfinish(conn); @@ -281,12 +289,19 @@ vacuum_one_database(ConnParams *cparams, if (!quiet) { + /* XXX get rid of the assumption that ANALYZE_NO_STAGE means vacuum */ if (stage != ANALYZE_NO_STAGE) printf(_("%s: processing database \"%s\": %s\n"), progname, PQdb(conn), _(stage_messages[stage])); - else + else if (vacopts->mode == MODE_VACUUM) printf(_("%s: vacuuming database \"%s\"\n"), progname, PQdb(conn)); + else + { + Assert(vacopts->mode == MODE_REPACK); + printf(_("%s: repacking database \"%s\"\n"), + progname, PQdb(conn)); + } fflush(stdout); } @@ -376,7 +391,7 @@ vacuum_one_database(ConnParams *cparams, * through ParallelSlotsGetIdle. */ ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); - run_vacuum_command(free_slot->connection, sql.data, + run_vacuum_command(free_slot->connection, vacopts, sql.data, echo, tabname); cell = cell->next; @@ -389,7 +404,8 @@ vacuum_one_database(ConnParams *cparams, } /* If we used SKIP_DATABASE_STATS, mop up with ONLY_DATABASE_STATS */ - if (vacopts->skip_database_stats && stage == ANALYZE_NO_STAGE && + if (vacopts->mode == MODE_VACUUM && + vacopts->skip_database_stats && stage == ANALYZE_NO_STAGE && !vacopts->analyze_only) { const char *cmd = "VACUUM (ONLY_DATABASE_STATS);"; @@ -402,7 +418,7 @@ vacuum_one_database(ConnParams *cparams, } ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); - run_vacuum_command(free_slot->connection, cmd, echo, NULL); + run_vacuum_command(free_slot->connection, vacopts, cmd, echo, NULL); if (!ParallelSlotsWaitCompletion(sa)) ret = EXIT_FAILURE; @@ -624,6 +640,23 @@ retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, " AND listed_objects.object_oid IS NOT NULL\n"); } + /* + * In REPACK mode, if the 'using_index' option was given but no index + * name, filter only tables that have an index with indisclustered set. + * (If an index name is given, we trust the user to pass a reasonable list + * of tables.) + * + * XXX it may be worth printing an error if an index name is given with + * no list of tables. + */ + if (vacopts->mode == MODE_REPACK && + vacopts->using_index && !vacopts->indexname) + { + appendPQExpBufferStr(&catalog_query, + " AND EXISTS (SELECT 1 FROM pg_catalog.pg_index\n" + " WHERE indrelid = c.oid AND indisclustered)\n"); + } + /* * If no tables were listed, filter for the relevant relation types. If * tables were given via --table, don't bother filtering by relation type. @@ -811,7 +844,30 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, resetPQExpBuffer(sql); - if (vacopts->analyze_only) + if (vacopts->mode == MODE_REPACK) + { + appendPQExpBufferStr(sql, "REPACK"); + + if (vacopts->verbose) + { + appendPQExpBuffer(sql, "%sVERBOSE", sep); + sep = comma; + } + + if (sep != paren) + appendPQExpBufferChar(sql, ')'); + + appendPQExpBuffer(sql, " %s", table); + + if (vacopts->using_index) + { + appendPQExpBuffer(sql, " USING INDEX"); + if (vacopts->indexname) + appendPQExpBuffer(sql, " %s", fmtIdEnc(vacopts->indexname, + PQclientEncoding(conn))); + } + } + else if (vacopts->analyze_only) { appendPQExpBufferStr(sql, "ANALYZE"); @@ -962,7 +1018,10 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, } } - appendPQExpBuffer(sql, " %s;", table); + if (vacopts->mode != MODE_REPACK) + appendPQExpBuffer(sql, " %s", table); + + appendPQExpBufferChar(sql, ';'); } /* @@ -972,8 +1031,8 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, * Any errors during command execution are reported to stderr. */ static void -run_vacuum_command(PGconn *conn, const char *sql, bool echo, - const char *table) +run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts, + const char *sql, bool echo, const char *table) { bool status; @@ -986,13 +1045,21 @@ run_vacuum_command(PGconn *conn, const char *sql, bool echo, { if (table) { - pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s", - table, PQdb(conn), PQerrorMessage(conn)); + if (vacopts->mode == MODE_VACUUM) + pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s", + table, PQdb(conn), PQerrorMessage(conn)); + else + pg_log_error("repacking of table \"%s\" in database \"%s\" failed: %s", + table, PQdb(conn), PQerrorMessage(conn)); } else { - pg_log_error("vacuuming of database \"%s\" failed: %s", - PQdb(conn), PQerrorMessage(conn)); + if (vacopts->mode == MODE_VACUUM) + pg_log_error("vacuuming of database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); + else + pg_log_error("repacking of database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); } } } diff --git a/src/bin/scripts/vacuuming.h b/src/bin/scripts/vacuuming.h index 021953e153a..dde68fb4cba 100644 --- a/src/bin/scripts/vacuuming.h +++ b/src/bin/scripts/vacuuming.h @@ -17,6 +17,12 @@ #include "fe_utils/connect_utils.h" #include "fe_utils/simple_list.h" +typedef enum +{ + MODE_VACUUM, + MODE_REPACK +} RunMode; + /* For analyze-in-stages mode */ #define ANALYZE_NO_STAGE -1 #define ANALYZE_NUM_STAGES 3 @@ -24,11 +30,14 @@ /* vacuum options controlled by user flags */ typedef struct vacuumingOptions { + RunMode mode; bool analyze_only; bool verbose; bool and_analyze; bool full; bool freeze; + bool using_index; + char *indexname; bool disable_page_skipping; bool skip_locked; int min_xid_age; diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index 60088a64cbb..890998d84bb 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -24,6 +24,7 @@ #define CLUOPT_RECHECK 0x02 /* recheck relation state */ #define CLUOPT_RECHECK_ISCLUSTERED 0x04 /* recheck relation state for * indisclustered */ +#define CLUOPT_ANALYZE 0x08 /* do an ANALYZE */ /* options for CLUSTER */ typedef struct ClusterParams @@ -31,8 +32,11 @@ typedef struct ClusterParams bits32 options; /* bitmask of CLUOPT_* */ } ClusterParams; -extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel); -extern void cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params); + +extern void ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel); + +extern void cluster_rel(RepackCommand command, bool usingindex, + Relation OldHeap, Oid indexOid, ClusterParams *params); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index 1cde4bd9bcf..5b6639c114c 100644 --- a/src/include/commands/progress.h +++ b/src/include/commands/progress.h @@ -56,24 +56,51 @@ #define PROGRESS_ANALYZE_PHASE_COMPUTE_EXT_STATS 4 #define PROGRESS_ANALYZE_PHASE_FINALIZE_ANALYZE 5 -/* Progress parameters for cluster */ -#define PROGRESS_CLUSTER_COMMAND 0 -#define PROGRESS_CLUSTER_PHASE 1 -#define PROGRESS_CLUSTER_INDEX_RELID 2 -#define PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED 3 -#define PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN 4 -#define PROGRESS_CLUSTER_TOTAL_HEAP_BLKS 5 -#define PROGRESS_CLUSTER_HEAP_BLKS_SCANNED 6 -#define PROGRESS_CLUSTER_INDEX_REBUILD_COUNT 7 +/* + * Progress parameters for REPACK. + * + * Note: Since REPACK shares some code with CLUSTER, these values are also + * used by CLUSTER. (CLUSTER is now deprecated, so it makes little sense to + * introduce a separate set of constants.) + */ +#define PROGRESS_REPACK_COMMAND 0 +#define PROGRESS_REPACK_PHASE 1 +#define PROGRESS_REPACK_INDEX_RELID 2 +#define PROGRESS_REPACK_HEAP_TUPLES_SCANNED 3 +#define PROGRESS_REPACK_HEAP_TUPLES_WRITTEN 4 +#define PROGRESS_REPACK_TOTAL_HEAP_BLKS 5 +#define PROGRESS_REPACK_HEAP_BLKS_SCANNED 6 +#define PROGRESS_REPACK_INDEX_REBUILD_COUNT 7 -/* Phases of cluster (as advertised via PROGRESS_CLUSTER_PHASE) */ -#define PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP 1 -#define PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP 2 -#define PROGRESS_CLUSTER_PHASE_SORT_TUPLES 3 -#define PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP 4 -#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES 5 -#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX 6 -#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP 7 +/* + * Phases of repack (as advertised via PROGRESS_REPACK_PHASE). + */ +#define PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP 1 +#define PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP 2 +#define PROGRESS_REPACK_PHASE_SORT_TUPLES 3 +#define PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP 4 +#define PROGRESS_REPACK_PHASE_SWAP_REL_FILES 5 +#define PROGRESS_REPACK_PHASE_REBUILD_INDEX 6 +#define PROGRESS_REPACK_PHASE_FINAL_CLEANUP 7 + +/* + * Commands of PROGRESS_REPACK + * + * Currently we only have one command, so the PROGRESS_REPACK_COMMAND + * parameter is not necessary. However it makes cluster.c simpler if we have + * the same set of parameters for CLUSTER and REPACK - see the note on REPACK + * parameters above. + */ +#define PROGRESS_REPACK_COMMAND_REPACK 1 + +/* + * Progress parameters for cluster. + * + * Although we need to report REPACK and CLUSTER in separate views, the + * parameters and phases of CLUSTER are a subset of those of REPACK. Therefore + * we just use the appropriate values defined for REPACK above instead of + * defining a separate set of constants here. + */ /* Commands of PROGRESS_CLUSTER */ #define PROGRESS_CLUSTER_COMMAND_CLUSTER 1 diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4ed14fc5b78..e4ba59f6b8f 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3949,16 +3949,26 @@ typedef struct AlterSystemStmt } AlterSystemStmt; /* ---------------------- - * Cluster Statement (support pbrown's cluster index implementation) + * Repack Statement * ---------------------- */ -typedef struct ClusterStmt +typedef enum RepackCommand +{ + REPACK_COMMAND_CLUSTER, + REPACK_COMMAND_REPACK, + REPACK_COMMAND_VACUUMFULL, +} RepackCommand; + +typedef struct RepackStmt { NodeTag type; - RangeVar *relation; /* relation being indexed, or NULL if all */ - char *indexname; /* original index defined */ + RepackCommand command; /* type of command being run */ + RangeVar *relation; /* relation being repacked */ + char *indexname; /* order tuples by this index */ + bool usingindex; /* whether USING INDEX is specified */ List *params; /* list of DefElem nodes */ -} ClusterStmt; +} RepackStmt; + /* ---------------------- * Vacuum and Analyze Statements diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index a4af3f717a1..22559369e2c 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -374,6 +374,7 @@ PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("repack", REPACK, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD, BARE_LABEL) diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h index d250a714d59..cceb312f2b3 100644 --- a/src/include/tcop/cmdtaglist.h +++ b/src/include/tcop/cmdtaglist.h @@ -196,6 +196,7 @@ PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false) PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false) PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", true, false, false) PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false) +PG_CMDTAG(CMDTAG_REPACK, "REPACK", false, false, false) PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false) PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false) PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false) diff --git a/src/include/utils/backend_progress.h b/src/include/utils/backend_progress.h index dda813ab407..e69e366dcdc 100644 --- a/src/include/utils/backend_progress.h +++ b/src/include/utils/backend_progress.h @@ -28,6 +28,7 @@ typedef enum ProgressCommandType PROGRESS_COMMAND_CREATE_INDEX, PROGRESS_COMMAND_BASEBACKUP, PROGRESS_COMMAND_COPY, + PROGRESS_COMMAND_REPACK, } ProgressCommandType; #define PGSTAT_NUM_PROGRESS_PARAM 20 diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 4d40a6809ab..5256628b51d 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -254,6 +254,63 @@ ORDER BY 1; clstr_tst_pkey (3 rows) +-- REPACK handles individual tables identically to CLUSTER, but it's worth +-- checking if it handles table hierarchies identically as well. +REPACK clstr_tst USING INDEX clstr_tst_c; +-- Verify that inheritance link still works +INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table 2'); +SELECT a,b,c,substring(d for 30), length(d) from clstr_tst; + a | b | c | substring | length +----+-----+------------------+--------------------------------+-------- + 10 | 14 | catorce | | + 18 | 5 | cinco | | + 9 | 4 | cuatro | | + 26 | 19 | diecinueve | | + 12 | 18 | dieciocho | | + 30 | 16 | dieciseis | | + 24 | 17 | diecisiete | | + 2 | 10 | diez | | + 23 | 12 | doce | | + 11 | 2 | dos | | + 25 | 9 | nueve | | + 31 | 8 | ocho | | + 1 | 11 | once | | + 28 | 15 | quince | | + 32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000 + 29 | 7 | siete | | + 15 | 13 | trece | | + 22 | 30 | treinta | | + 17 | 32 | treinta y dos | | + 3 | 31 | treinta y uno | | + 5 | 3 | tres | | + 20 | 1 | uno | | + 6 | 20 | veinte | | + 14 | 25 | veinticinco | | + 21 | 24 | veinticuatro | | + 4 | 22 | veintidos | | + 19 | 29 | veintinueve | | + 16 | 28 | veintiocho | | + 27 | 26 | veintiseis | | + 13 | 27 | veintisiete | | + 7 | 23 | veintitres | | + 8 | 21 | veintiuno | | + 0 | 100 | in child table | | + 0 | 100 | in child table 2 | | +(34 rows) + +-- Verify that foreign key link still works +INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); +ERROR: insert or update on table "clstr_tst" violates foreign key constraint "clstr_tst_con" +DETAIL: Key (b)=(1111) is not present in table "clstr_tst_s". +SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass +ORDER BY 1; + conname +---------------------- + clstr_tst_a_not_null + clstr_tst_con + clstr_tst_pkey +(3 rows) + SELECT relname, relkind, EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname; @@ -381,6 +438,35 @@ SELECT * FROM clstr_1; 2 (2 rows) +-- REPACK w/o argument performs no ordering, so we can only check which tables +-- have the relfilenode changed. +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_old AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); +SET SESSION AUTHORIZATION regress_clstr_user; +SET client_min_messages = ERROR; -- order of "skipping" warnings may vary +REPACK; +RESET client_min_messages; +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_new AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); +-- Do the actual comparison. Unlike CLUSTER, clstr_3 should have been +-- processed because there is nothing like clustering index here. +SELECT o.relname FROM relnodes_old o +JOIN relnodes_new n ON o.relname = n.relname +WHERE o.relfilenode <> n.relfilenode +ORDER BY o.relname; + relname +--------- + clstr_1 + clstr_3 +(2 rows) + +SET SESSION AUTHORIZATION regress_clstr_user; -- Test MVCC-safety of cluster. There isn't much we can do to verify the -- results with a single backend... CREATE TABLE clustertest (key int PRIMARY KEY); @@ -495,6 +581,43 @@ ALTER TABLE clstrpart SET WITHOUT CLUSTER; ERROR: cannot mark index clustered in partitioned table ALTER TABLE clstrpart CLUSTER ON clstrpart_idx; ERROR: cannot mark index clustered in partitioned table +-- Check that REPACK sets new relfilenodes: it should process exactly the same +-- tables as CLUSTER did. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart USING INDEX clstrpart_idx; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + relname | level | relkind | ?column? +-------------+-------+---------+---------- + clstrpart | 0 | p | t + clstrpart1 | 1 | p | t + clstrpart11 | 2 | r | f + clstrpart12 | 2 | p | t + clstrpart2 | 1 | r | f + clstrpart3 | 1 | p | t + clstrpart33 | 2 | r | f +(7 rows) + +-- And finally the same for REPACK w/o index. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + relname | level | relkind | ?column? +-------------+-------+---------+---------- + clstrpart | 0 | p | t + clstrpart1 | 1 | p | t + clstrpart11 | 2 | r | f + clstrpart12 | 2 | p | t + clstrpart2 | 1 | r | f + clstrpart3 | 1 | p | t + clstrpart33 | 2 | r | f +(7 rows) + DROP TABLE clstrpart; -- Ownership of partitions is checked CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); @@ -513,7 +636,7 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; -WARNING: permission denied to cluster "ptnowner2", skipping it +WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 35e8aad7701..3a1d1d28282 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2071,6 +2071,29 @@ pg_stat_progress_create_index| SELECT s.pid, s.param15 AS partitions_done FROM (pg_stat_get_progress_info('CREATE INDEX'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); +pg_stat_progress_repack| SELECT s.pid, + s.datid, + d.datname, + s.relid, + CASE s.param2 + WHEN 0 THEN 'initializing'::text + WHEN 1 THEN 'seq scanning heap'::text + WHEN 2 THEN 'index scanning heap'::text + WHEN 3 THEN 'sorting tuples'::text + WHEN 4 THEN 'writing new heap'::text + WHEN 5 THEN 'swapping relation files'::text + WHEN 6 THEN 'rebuilding index'::text + WHEN 7 THEN 'performing final cleanup'::text + ELSE NULL::text + END AS phase, + (s.param3)::oid AS repack_index_relid, + s.param4 AS heap_tuples_scanned, + s.param5 AS heap_tuples_written, + s.param6 AS heap_blks_total, + s.param7 AS heap_blks_scanned, + s.param8 AS index_rebuild_count + FROM (pg_stat_get_progress_info('REPACK'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) + LEFT JOIN pg_database d ON ((s.datid = d.oid))); pg_stat_progress_vacuum| SELECT s.pid, s.datid, d.datname, diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index b7115f86104..cfcc3dc9761 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -76,6 +76,19 @@ INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass ORDER BY 1; +-- REPACK handles individual tables identically to CLUSTER, but it's worth +-- checking if it handles table hierarchies identically as well. +REPACK clstr_tst USING INDEX clstr_tst_c; + +-- Verify that inheritance link still works +INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table 2'); +SELECT a,b,c,substring(d for 30), length(d) from clstr_tst; + +-- Verify that foreign key link still works +INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); + +SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass +ORDER BY 1; SELECT relname, relkind, EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast @@ -159,6 +172,34 @@ INSERT INTO clstr_1 VALUES (1); CLUSTER clstr_1; SELECT * FROM clstr_1; +-- REPACK w/o argument performs no ordering, so we can only check which tables +-- have the relfilenode changed. +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_old AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); + +SET SESSION AUTHORIZATION regress_clstr_user; +SET client_min_messages = ERROR; -- order of "skipping" warnings may vary +REPACK; +RESET client_min_messages; + +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_new AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); + +-- Do the actual comparison. Unlike CLUSTER, clstr_3 should have been +-- processed because there is nothing like clustering index here. +SELECT o.relname FROM relnodes_old o +JOIN relnodes_new n ON o.relname = n.relname +WHERE o.relfilenode <> n.relfilenode +ORDER BY o.relname; + +SET SESSION AUTHORIZATION regress_clstr_user; + -- Test MVCC-safety of cluster. There isn't much we can do to verify the -- results with a single backend... @@ -229,6 +270,24 @@ SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM o CLUSTER clstrpart; ALTER TABLE clstrpart SET WITHOUT CLUSTER; ALTER TABLE clstrpart CLUSTER ON clstrpart_idx; + +-- Check that REPACK sets new relfilenodes: it should process exactly the same +-- tables as CLUSTER did. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart USING INDEX clstrpart_idx; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + +-- And finally the same for REPACK w/o index. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + DROP TABLE clstrpart; -- Ownership of partitions is checked diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 3c80d49b67e..c17b5c0cadc 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2537,6 +2537,8 @@ ReorderBufferTupleCidKey ReorderBufferUpdateProgressTxnCB ReorderTuple RepOriginId +RepackCommand +RepackStmt ReparameterizeForeignPathByChild_function ReplaceVarsFromTargetList_context ReplaceVarsNoMatchOption @@ -2604,6 +2606,7 @@ RtlNtStatusToDosError_t RuleInfo RuleLock RuleStmt +RunMode RunningTransactions RunningTransactionsData SASLStatus -- 2.47.3 --52vaejy5wnlkvc5v-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
* [PATCH v10 2/2] review @ 2026-02-06 09:24 Álvaro Herrera <alvherre@kurilemu.de> 0 siblings, 0 replies; 190+ messages in thread From: Álvaro Herrera @ 2026-02-06 09:24 UTC (permalink / raw) --- doc/src/sgml/config.sgml | 18 ++-- src/backend/commands/variable.c | 127 +++++++++++------------- src/backend/postmaster/launch_backend.c | 2 +- src/backend/utils/init/miscinit.c | 2 +- src/backend/utils/misc/guc_tables.c | 14 +-- src/include/postmaster/proctypelist.h | 38 +++---- src/test/regress/expected/guc.out | 8 +- 7 files changed, 93 insertions(+), 116 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cbc748a3636..984194ca758 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7090,15 +7090,15 @@ local0.* /var/log/postgresql </term> <listitem> <para> - Controls which <link linkend="runtime-config-severity-levels">message - levels</link> are written to the server log. - Valid values are a comma-separated list of <literal>type:level</literal> - and a single <literal>level</literal>. The list allows it to use - different levels per process type. Only the single <literal>level</literal> - is mandatory (order does not matter) and it is assigned to the process - types that are not specified in the list. - Valid process types are listed in the table below, each corresponding to - either postmaster, an auxiliary process type or a backend. + Controls which + <link linkend="runtime-config-severity-levels">message levels</link> + are written to the server log. Valid values are a comma-separated + list of zero or more + <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal> + entries and exactly one mandatory + <literal><replaceable>level</replaceable></literal> entry, + which is the default for process types not listed. + Valid process types are listed in the table below. <simplelist type="vert" columns="4"> <member><literal>archiver</literal></member> <member><literal>autovacuum</literal></member> diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index a450254d1fa..97b9a6b5964 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source) /* * GUC check_hook for log_min_messages * - * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE - * is log_min_messages_process_types. LEVEL is server_message_level_options. A - * single LEVEL element should be part of this list and it is applied as a - * final step to the process types that are not specified. For backward - * compatibility, the old syntax is still accepted and it means to apply the - * LEVEL for all process types. + * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL + * elements. For each element, TYPE corresponds to a bk_category value (see + * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls. + * + * In addition, there must be a single LEVEL element (with no TYPE part) + * which sets the default level for process types that aren't specified. */ bool check_log_min_messages(char **newval, void **extra, GucSource source) @@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source) List *elemlist; StringInfoData buf; char *result; - bool first = true; int newlevel[BACKEND_NUM_TYPES]; - bool assigned[BACKEND_NUM_TYPES]; + bool assigned[BACKEND_NUM_TYPES] = {0}; int genericlevel = -1; /* -1 means not assigned */ - /* Initialize the array. */ - for (int i = 0; i < BACKEND_NUM_TYPES; i++) - { - newlevel[i] = WARNING; - assigned[i] = false; - } + const char *const process_types[] = { +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = bkcategory, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE + }; /* Need a modifiable copy of string. */ rawstring = guc_strdup(LOG, *newval); - /* Parse string into list of identifiers. */ + /* Parse the string into a list. */ if (!SplitGUCList(rawstring, ',', &elemlist)) { /* syntax error in list */ @@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source) /* Validate and assign log level and process type. */ foreach_ptr(char, tok, elemlist) { - char *sep; - const struct config_enum_entry *entry; + char *sep = strchr(tok, ':'); /* - * Check whether there is a process type following the log level. If - * there is no separator, it means this is the generic log level. The - * generic log level will be assigned to the process types that were - * not informed. + * If there's no ':' separator in the entry, this is the default + * value. Otherwise it's a process type-specific entry. */ - sep = strchr(tok, ':'); if (sep == NULL) { + const struct config_enum_entry *entry; bool found = false; /* Reject duplicates for generic log level. */ if (genericlevel != -1) { - GUC_check_errdetail("Generic log level was already assigned."); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant specification of default log level."); + goto lmm_fail; } /* Is the log level valid? */ @@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { GUC_check_errdetail("Unrecognized log level: \"%s\".", tok); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } } else @@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source) char *loglevel; char *ptype; bool found = false; + int level; + const struct config_enum_entry *entry; - ptype = guc_malloc(LOG, (sep - tok) + 1); - if (!ptype) - { - guc_free(rawstring); - list_free(elemlist); - return false; - } - memcpy(ptype, tok, sep - tok); - ptype[sep - tok] = '\0'; + ptype = tok; loglevel = sep + 1; + *sep = '\0'; /* Is the log level valid? */ for (entry = server_message_level_options; entry && entry->name; entry++) { if (pg_strcasecmp(entry->name, loglevel) == 0) { + level = entry->val; found = true; break; } @@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source) if (!found) { - GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".", + ptype, loglevel); + goto lmm_fail; } - /* - * Is the process type name valid? There might be multiple entries - * per process type, don't bail out because it can assign the - * value for multiple entries. - */ + /* Is the process type name valid and unique? */ found = false; for (int i = 0; i < BACKEND_NUM_TYPES; i++) { - if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0) + if (pg_strcasecmp(process_types[i], ptype) == 0) { /* Reject duplicates for a process type. */ if (assigned[i]) { - GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + GUC_check_errdetail("Redundant log level specification for process type \"%s\".", + ptype); + goto lmm_fail; } - newlevel[i] = entry->val; + newlevel[i] = level; assigned[i] = true; found = true; + break; } } if (!found) { GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype); - guc_free(ptype); - guc_free(rawstring); - list_free(elemlist); - return false; + goto lmm_fail; } - guc_free(ptype); + /* Put the separator back in place */ + *sep = ':'; } + + /* all good */ + continue; + +lmm_fail: + guc_free(rawstring); + list_free(elemlist); + return false; } /* @@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source) */ if (genericlevel == -1) { - GUC_check_errdetail("Generic log level was not defined."); + GUC_check_errdetail("Default log level was not defined."); guc_free(rawstring); list_free(elemlist); return false; @@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source) } /* - * Use a stable representation of log_min_messages. The generic level is - * always the first element and the other elements (type:level) are sorted - * by process type. See log_min_messages_cmp for details. + * To present a nice view to users, sort the output list by process type, + * with the default value first. */ list_sort(elemlist, log_min_messages_cmp); - initStringInfo(&buf); + initStringInfoExt(&buf, strlen(rawstring) + 1); foreach_ptr(char, tok, elemlist) { - if (first) - { + if (foreach_current_index(tok) == 0) appendStringInfoString(&buf, tok); - first = false; - } else - { appendStringInfo(&buf, ", %s", tok); - } } result = (char *) guc_malloc(LOG, buf.len + 1); @@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source) return true; } +/* + * list_sort() callback for check_log_min_messages. The default element + * goes first; the rest are ordered by strcmp() of the process type. + */ static int log_min_messages_cmp(const ListCell *a, const ListCell *b) { diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index 08553c0f024..05b1feef3cf 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -178,7 +178,7 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ [bktype] = {description, main_func, shmem_attach}, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index efba20fa8b9..03f6c8479f2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ case bktype: backendDesc = description; break; #include "postmaster/proctypelist.h" #undef PG_PROCTYPE diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 0aebb9c8e24..53933f53b25 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -648,23 +648,15 @@ char *role_string; bool in_hot_standby_guc; /* - * log_min_messages + * set default log_min_messages to WARNING for all process types */ int log_min_messages[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = log_min_messages, +#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \ + [bktype] = WARNING, #include "postmaster/proctypelist.h" #undef PG_PROCTYPE }; -const char *const log_min_messages_process_types[] = { -#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \ - [bktype] = bkcategory, -#include "postmaster/proctypelist.h" -#undef PG_PROCTYPE -}; - - /* * Displayable names for context types (enum GucContext) * diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h index 0ddd805f0e7..4e259e84c2d 100644 --- a/src/include/postmaster/proctypelist.h +++ b/src/include/postmaster/proctypelist.h @@ -30,22 +30,22 @@ */ -/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */ -PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING) -PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING) -PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING) -PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING) -PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING) -PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING) -PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING) -PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING) -PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING) -PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING) -PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING) -PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING) -PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING) -PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING) -PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING) -PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING) +/* bktype, bkcategory, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true) +PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false) +PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true) diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 9a6fd503009..616b10f80d6 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -955,19 +955,19 @@ SHOW log_min_messages; SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; --fail ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1" -DETAIL: Generic log level was not defined. +DETAIL: Default log level was not defined. SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal" -DETAIL: Generic log level was already assigned. +DETAIL: Redundant specification of default log level. SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning" -DETAIL: Process type "backend" was already assigned. +DETAIL: Redundant log level specification for process type "backend". SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1" DETAIL: Unrecognized process type: "foo". SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1" -DETAIL: Unrecognized log level: "bar". +DETAIL: Unrecognized log level for process type "checkpointer": "bar". SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3'; SHOW log_min_messages; log_min_messages -- 2.47.3 --r3qaph7b72ke2fw4-- ^ permalink raw reply [nested|flat] 190+ messages in thread
end of thread, other threads:[~2026-02-06 09:24 UTC | newest] Thread overview: 190+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2018-10-28 15:09 [PATCH 2/2] review Tomas Vondra <tomas@2ndquadrant.com> 2018-10-28 17:38 [PATCH 2/2] review Tomas Vondra <tomas@2ndquadrant.com> 2021-01-09 02:17 [PATCH 1/2] review Tomas Vondra <tomas@2ndquadrant.com> 2021-03-09 20:09 [PATCH 2/2] review Tomas Vondra <tomas.vondra@postgresql.org> 2021-03-16 16:29 [PATCH 02/10] review Tomas Vondra <tomas.vondra@postgresql.org> 2021-03-16 18:26 [PATCH 04/10] review Tomas Vondra <tomas.vondra@postgresql.org> 2021-03-17 01:19 [PATCH 06/10] review Tomas Vondra <tomas.vondra@postgresql.org> 2021-03-17 01:36 [PATCH 08/10] review Tomas Vondra <tomas.vondra@postgresql.org> 2025-04-03 18:14 [PATCH 2/2] review Álvaro Herrera <alvherre@alvh.no-ip.org> 2025-07-26 17:57 [PATCH v22 2/2] Add REPACK command Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de> 2026-02-06 09:24 [PATCH v10 2/2] review Álvaro Herrera <alvherre@kurilemu.de>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox