src/backend/nodes/outfuncs.c - Coverage Report

← Back to Overview

# src/backend/nodes/outfuncs.c

Coverage: 28/30 lines (93.3%)
Total Lines
30
modified
Covered
28
93.3%
Uncovered
2
6.7%
Keyboard navigation
_outRPRPattern() lines 732-779
Modified Lines Coverage: 28/30 lines (93.3%)

Line | Hits | Source | Commit
--- | --- | --- | ---
732 | 2980 | _outRPRPattern(StringInfo str, const RPRPattern *node) | c54ba27Row pattern recognition patch (parse/analysis).
733 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
734 | 2980 | WRITE_NODE_TYPE("RPRPATTERN"); | c54ba27Row pattern recognition patch (parse/analysis).
735 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
736 | 2980 | WRITE_INT_FIELD(numVars); | c54ba27Row pattern recognition patch (parse/analysis).
737 | 2980 | WRITE_INT_FIELD(maxDepth); | c54ba27Row pattern recognition patch (parse/analysis).
738 | 2980 | WRITE_INT_FIELD(numElements); | c54ba27Row pattern recognition patch (parse/analysis).
739 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
740 | - | /* Write varNames array as list of strings */ | c54ba27Row pattern recognition patch (parse/analysis).
741 | 2980 | appendStringInfoString(str, " :varNames"); | c54ba27Row pattern recognition patch (parse/analysis).
742 | 2980 | if (node->numVars > 0 && node->varNames != NULL) | c54ba27Row pattern recognition patch (parse/analysis).
743 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
744 | 2980 | appendStringInfoString(str, " ("); | c54ba27Row pattern recognition patch (parse/analysis).
745 | 10116 | for (int i = 0; i < node->numVars; i++) | c54ba27Row pattern recognition patch (parse/analysis).
746 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
747 | 7136 | if (i > 0) | c54ba27Row pattern recognition patch (parse/analysis).
748 | 4156 | appendStringInfoChar(str, ' '); | c54ba27Row pattern recognition patch (parse/analysis).
749 | 7136 | outToken(str, node->varNames[i]); | c54ba27Row pattern recognition patch (parse/analysis).
750 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
751 | 2980 | appendStringInfoChar(str, ')'); | c54ba27Row pattern recognition patch (parse/analysis).
752 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
753 | - | else | c54ba27Row pattern recognition patch (parse/analysis).
754 | 0 | appendStringInfoString(str, " <>"); | c54ba27Row pattern recognition patch (parse/analysis).
| UnreachableDefensive (unreachable) · confidence high · `_outRPRPattern @754` Reason
    Confirmed defensive-unreachable.
    Line 754 (`appendStringInfoString(str, " <>")`) is the else branch of the guard `if (node->numVars > 0 && node->varNames != NULL)` at outfuncs.c:742. It executes only when an RPRPattern has numVars <= 0 OR varNames == NULL.
    I traced every construction path for RPRPattern:
    (1) makeRPRPattern() at rpr.c:1127 is the SOLE original constructor (only call site rpr.c:1938), and it Asserts numVars > 0 (rpr.c:1143) then unconditionally palloc's varNames (rpr.c:1144-1146);
    (2) _copyRPRPattern() at copyfuncs.c:171 copies an existing node, Asserts from->numVars > 0 (copyfuncs.c:180) and always palloc's newnode->varNames (copyfuncs.c:181);
    (3) the read path reconstructs from out output. numVars is derived from scanRPRPattern over the
    PATTERN tree plus collectDefineVariables;
    a
    PATTERN clause cannot be syntactically empty, so there is always >= 1 primary variable, hence numVars is never 0 and varNames is never NULL.
    No SQL/regression input can drive an RPRPattern with zero variables to _outNode, so line 754 cannot be reached.
    I could not construct any refuting input.
    Note: an identical defensive else exists for :elements at line 776 (also guarded by numElements>=2 Asserts), so this is a consistent defensive idiom rather than a logic flaw.
Recommended fix
    Because makeRPRPattern (rpr.c:1143-1144) and _copyRPRPattern (copyfuncs.c:180-181) already enforce numVars > 0 and a non-NULL varNames, the empty-array case at outfuncs.c:753-754 cannot occur.
    Option A (minimal, keeps defensive style): leave as-is and accept the line as documented defensive-unreachable, matching the parallel :elements else at line 776. Option B (eliminate the branch): replace the guard with `Assert(node->numVars > 0 && node->varNames != NULL);
    ` before the loop and emit the `( ... )` list unconditionally (drop lines 753-754), and likewise simplify the matching reader at readfuncs.c:595. If pursuing Option B, do the same to the :elements branch (lines 758,775-776) for consistency, since both rest on the same makeRPRPattern/_copyRPRPattern invariants (numElements >= 2 at rpr.c:1149, copyfuncs.c:186).
    Recommend Option A unless the project wants to actively reduce defensive-unreachable line count.
755 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
756 | - | /* Write elements array */ | c54ba27Row pattern recognition patch (parse/analysis).
757 | 2980 | appendStringInfoString(str, " :elements"); | c54ba27Row pattern recognition patch (parse/analysis).
758 | 2980 | if (node->numElements > 0 && node->elements != NULL) | c54ba27Row pattern recognition patch (parse/analysis).
759 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
760 | 2980 | appendStringInfoChar(str, ' '); | c54ba27Row pattern recognition patch (parse/analysis).
761 | 17380 | for (int i = 0; i < node->numElements; i++) | c54ba27Row pattern recognition patch (parse/analysis).
762 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
763 | 14400 | const RPRPatternElement *elem = &node->elements[i]; | c54ba27Row pattern recognition patch (parse/analysis).
764 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
765 | 14400 | appendStringInfo(str, "(%d %d %u %d %d %d %d)", | c54ba27Row pattern recognition patch (parse/analysis).
766 | 14400 | (int) elem->varId, | c54ba27Row pattern recognition patch (parse/analysis).
767 | 14400 | (int) elem->depth, | c54ba27Row pattern recognition patch (parse/analysis).
768 | 14400 | (unsigned) elem->flags, | c54ba27Row pattern recognition patch (parse/analysis).
769 | 14400 | (int) elem->min, | c54ba27Row pattern recognition patch (parse/analysis).
770 | 14400 | (int) elem->max, | c54ba27Row pattern recognition patch (parse/analysis).
771 | 14400 | (int) elem->next, | c54ba27Row pattern recognition patch (parse/analysis).
772 | 14400 | (int) elem->jump); | c54ba27Row pattern recognition patch (parse/analysis).
773 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
774 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
775 | - | else | c54ba27Row pattern recognition patch (parse/analysis).
776 | 0 | appendStringInfoString(str, " <>"); | c54ba27Row pattern recognition patch (parse/analysis).
| UnreachableDefensive (unreachable) · confidence high · `_outRPRPattern @776` Reason
    CONFIRMED unreachable from any SQL/regression input.
    The else at outfuncs.c:776 emits " <>" only when (node->numElements <= 0 || node->elements == NULL).
    The sole RPRPattern producer is makeRPRPattern() in optimizer/plan/rpr.c (only makeNode(RPRPattern) besides the mechanical copyfuncs.c copy).
    It Asserts numElements >= 2 (rpr.c:1149) and unconditionally palloc0's the elements array (rpr.c:1150). numElements is computed by scanRPRPattern(), which always adds +1 for the FIN marker (rpr.c:1110) on top of >= 1 VAR element (every RPR_PATTERN_VAR leaf does (*numElements)++ at rpr.c:1030;
    a valid
    PATTERN must contain at least one variable).
    So numElements is always >= 2 and elements is never NULL for any serialized node. copyfuncs.c preserves these fields verbatim under -DCOPY_PARSE_PLAN_TREES, so the COPY/WRITE_READ path cannot zero them either.
    I attempted to refute by searching for any path that sets numElements<=0 or elements=NULL on a live node and found none.
    Note: the claim cited reader line 647;
    the actual reader else is readfuncs.c:645-648 (and the array loop is 599-644) -- minor line-number drift, substance is correct.
Recommended fix
    Since makeRPRPattern guarantees numElements >= 2 and elements != NULL, replace the runtime if/else with an unconditional array write guarded by an assertion: add Assert(node->numElements > 0 && node->elements != NULL);
    before the loop, drop the if-condition and the else that emits " <>".
    Keep the reader in sync by removing the corresponding else (readfuncs.c:645-648) and the "token[0] == '('" / "numElements > 0" guard at readfuncs.c:601, since the writer will always emit '('.
    Alternatively, if maximum robustness under WRITE_READ_PARSE_PLAN_TREES is preferred, leave both branches as-is and simply annotate line 776 as defensive (e.g., exclude from coverage), since the cost of the dead branch is negligible and it hardens against future producers.
777 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
778 | 4848 | WRITE_BOOL_FIELD(isAbsorbable); | c54ba27Row pattern recognition patch (parse/analysis).
779 | 2980 | } | c54ba27Row pattern recognition patch (parse/analysis).