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

← Back to Overview

# src/backend/nodes/readfuncs.c

Coverage: 46/48 lines (95.8%)
Total Lines
48
modified
Covered
46
95.8%
Uncovered
2
4.2%
Keyboard navigation
_readRPRPattern() lines 572-653
Modified Lines Coverage: 46/48 lines (95.8%)

Line | Hits | Source | Commit
--- | --- | --- | ---
572 | 2980 | _readRPRPattern(void) | c54ba27Row pattern recognition patch (parse/analysis).
573 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
574 | 2980 | READ_LOCALS(RPRPattern); | c54ba27Row pattern recognition patch (parse/analysis).
575 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
576 | 2980 | READ_INT_FIELD(numVars); | c54ba27Row pattern recognition patch (parse/analysis).
577 | 2980 | READ_INT_FIELD(maxDepth); | c54ba27Row pattern recognition patch (parse/analysis).
578 | 2980 | READ_INT_FIELD(numElements); | c54ba27Row pattern recognition patch (parse/analysis).
579 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
580 | - | /* Read varNames array */ | c54ba27Row pattern recognition patch (parse/analysis).
581 | 2980 | token = pg_strtok(&length); /* skip :varNames */ | c54ba27Row pattern recognition patch (parse/analysis).
582 | 2980 | token = pg_strtok(&length); /* get '(' or '<>' */ | c54ba27Row pattern recognition patch (parse/analysis).
583 | 2980 | if (local_node->numVars > 0 && token[0] == '(') | c54ba27Row pattern recognition patch (parse/analysis).
584 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
585 | 2980 | local_node->varNames = palloc_array(char *, local_node->numVars); | c54ba27Row pattern recognition patch (parse/analysis).
586 | 10116 | for (int i = 0; i < local_node->numVars; i++) | c54ba27Row pattern recognition patch (parse/analysis).
587 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
588 | 7136 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
589 | 7136 | local_node->varNames[i] = debackslash(token, length); | c54ba27Row pattern recognition patch (parse/analysis).
590 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
591 | 2980 | token = pg_strtok(&length); /* skip ')' */ | c54ba27Row pattern recognition patch (parse/analysis).
592 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
593 | - | else | c54ba27Row pattern recognition patch (parse/analysis).
594 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
595 | 0 | local_node->varNames = NULL; | c54ba27Row pattern recognition patch (parse/analysis).
| UnreachableDefensive (unreachable) · confidence high · `_readRPRPattern @595` Reason
    Confirmed unreachable by any SQL/regression input.
    Line 595 (local_node->varNames = NULL) is the else branch of `if (local_node->numVars > 0 && token[0] == '(')` at readfuncs.c:583. It is reached only when the serialized varNames token is the empty-list marker "<>".
    That marker is written by _outRPRPattern (outfuncs.c:753-754) only when `node->numVars <= 0 || node->varNames == NULL`.
    Every RPRPattern is constructed via makeRPRPattern (rpr.c:1128) which asserts numVars > 0 (rpr.c:1143) and always palloc's varNames (rpr.c:1144), or via _copyRPRPattern (copyfuncs.c:170) which asserts from->numVars > 0 (copyfuncs.c:180) and copies a non-NULL array.
    The only other makeNode(RPRPattern) is inside makeRPRPattern itself.
    The grammar (gram.y:17583
    PATTERN '(' row_pattern ')', row_pattern:17625) cannot produce an empty pattern, and
    DEFINE is required, so numVars is always >= 1. Node-read input in this build comes solely from _out* output (COPY_PARSE_PLAN_TREES / WRITE_READ_PARSE_PLAN_TREES round-trips and stored rules);
    there is no SQL surface to feed an arbitrary "<>" token to the reader.
    Hence _outRPRPattern never emits "<>" for a real RPRPattern and line 595's else branch never executes.
    Tried to refute via copy/out/read round-trip and via deparse, but all paths guarantee numVars>0 and non-NULL varNames, so token[0]=='(' always holds at read time.
Recommended fix
    Keep read/out symmetry but document the invariant.
    In _readRPRPattern, replace the else body at line 595 with an assertion that records the can't-happen condition while preserving the NULL assignment for safety:

        else
        {
            /* "<>" marker is never emitted for a real RPRPattern (numVars > 0) */
            Assert(local_node->numVars == 0);
    local_node->varNames = NULL;
    }

    This mirrors the Assert(numVars > 0) invariants already present in makeRPRPattern (rpr.c:1143) and _copyRPRPattern (copyfuncs.c:180), marks the branch as structurally unreachable, and avoids changing behavior for the (impossible) malformed-input case.
    Alternatively, leave the code unchanged: the NULL assignment is standard defensive out/read symmetry and the uncovered line is expected and harmless.
596 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
597 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
598 | - | /* Read elements array */ | c54ba27Row pattern recognition patch (parse/analysis).
599 | 2980 | token = pg_strtok(&length); /* skip :elements */ | c54ba27Row pattern recognition patch (parse/analysis).
600 | 2980 | token = pg_strtok(&length); /* get '(' or '<>' */ | c54ba27Row pattern recognition patch (parse/analysis).
601 | 2980 | if (local_node->numElements > 0 && token[0] == '(') | c54ba27Row pattern recognition patch (parse/analysis).
602 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
603 | 2980 | local_node->elements = palloc0_array(RPRPatternElement, local_node->numElements); | c54ba27Row pattern recognition patch (parse/analysis).
604 | 17380 | for (int i = 0; i < local_node->numElements; i++) | c54ba27Row pattern recognition patch (parse/analysis).
605 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
606 | 14400 | RPRPatternElement *elem = &local_node->elements[i]; | c54ba27Row pattern recognition patch (parse/analysis).
607 | 14400 | int varId, | c54ba27Row pattern recognition patch (parse/analysis).
608 | - | flags, | c54ba27Row pattern recognition patch (parse/analysis).
609 | - | depth, | c54ba27Row pattern recognition patch (parse/analysis).
610 | - | min, | c54ba27Row pattern recognition patch (parse/analysis).
611 | - | max, | c54ba27Row pattern recognition patch (parse/analysis).
612 | - | next, | c54ba27Row pattern recognition patch (parse/analysis).
613 | - | jump; | c54ba27Row pattern recognition patch (parse/analysis).
614 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
615 | - | /* Parse "(varId depth flags min max next jump)" */ | c54ba27Row pattern recognition patch (parse/analysis).
616 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
617 | 14400 | varId = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
618 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
619 | 14400 | depth = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
620 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
621 | 14400 | flags = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
622 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
623 | 14400 | min = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
624 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
625 | 14400 | max = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
626 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
627 | 14400 | next = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
628 | 14400 | token = pg_strtok(&length); | c54ba27Row pattern recognition patch (parse/analysis).
629 | 14400 | jump = atoi(token); | c54ba27Row pattern recognition patch (parse/analysis).
630 | 14400 | token = pg_strtok(&length); /* skip ')' */ | c54ba27Row pattern recognition patch (parse/analysis).
631 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
632 | 14400 | elem->varId = (RPRVarId) varId; | c54ba27Row pattern recognition patch (parse/analysis).
633 | 14400 | elem->flags = (RPRElemFlags) flags; | c54ba27Row pattern recognition patch (parse/analysis).
634 | 14400 | elem->depth = (RPRDepth) depth; | c54ba27Row pattern recognition patch (parse/analysis).
635 | 14400 | elem->min = (RPRQuantity) min; | c54ba27Row pattern recognition patch (parse/analysis).
636 | 14400 | elem->max = (RPRQuantity) max; | c54ba27Row pattern recognition patch (parse/analysis).
637 | 14400 | elem->next = (RPRElemIdx) next; | c54ba27Row pattern recognition patch (parse/analysis).
638 | 14400 | elem->jump = (RPRElemIdx) jump; | c54ba27Row pattern recognition patch (parse/analysis).
639 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
640 | - | /* Read next element's '(' or end */ | c54ba27Row pattern recognition patch (parse/analysis).
641 | 14400 | if (i < local_node->numElements - 1) | c54ba27Row pattern recognition patch (parse/analysis).
642 | 14400 | token = pg_strtok(&length); /* get '(' */ | c54ba27Row pattern recognition patch (parse/analysis).
643 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
644 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
645 | - | else | c54ba27Row pattern recognition patch (parse/analysis).
646 | - | { | c54ba27Row pattern recognition patch (parse/analysis).
647 | 0 | local_node->elements = NULL; | c54ba27Row pattern recognition patch (parse/analysis).
| UnreachableDefensive (unreachable) · confidence high · `_readRPRPattern @647` Reason
    Confirmed unreachable.
    Line 647 (local_node->elements = NULL) is the else branch of `if (numElements > 0 && token[0] == '(')` at readfuncs.c:601, taken only when _outRPRPattern wrote the "<>" marker (outfuncs.c:776), which happens only when node->numElements <= 0 || node->elements == NULL.
    I verified the ONLY constructor of RPRPattern is makeRPRPattern (rpr.c:1128), which asserts numElements >= 2 (line 1149) and unconditionally palloc0's the elements array (line 1150). scanRPRPattern always does (*numElements)++ for the FIN marker (rpr.c:1110), and the non-empty
    PATTERN grammar guarantees at least one real element. _copyRPRPattern (copyfuncs.c:171) likewise asserts numElements >= 2 and deep-copies a non-NULL array.
    Thus every live RPRPattern has numElements >= 2 and non-NULL elements;
    _outRPRPattern always emits the '(...)' form, never "<>";
    and the reader's else branch can never execute for any node produced by the system, including under -DWRITE_READ_PARSE_PLAN_TREES which round-trips every constructed node.
    I attempted to refute this by searching for any alternate makeNode(RPRPattern) construction site or any numElements=0/elements=NULL assignment outside out/read funcs;
    none exist.
    The only way to reach line 647 is hand-feeding a malformed serialized string with ':numElements 0' or ':elements <>' directly to the reader, which no SQL/regression path does.
Recommended fix
    Replace the unreachable NULL-assignment else with an assertion documenting the invariant, mirroring the constructor/copy/out paths.
    E.g. change lines 645-648 to:
    `else { Assert(false);
    /* makeRPRPattern guarantees numElements >= 2 and non-NULL elements;
    _outRPRPattern never emits "<>" for the :elements field */ local_node->elements = NULL;
    }`.
    Keeping the NULL assignment after the Assert preserves defensiveness for non-assert builds.
    Alternatively, gate the populated block with Assert(local_node->numElements >= 2 && token[0] == '(') before line 601 and drop the else entirely.
    Note: the existing varNames else branch (line 595) is analogously defensive but reachable status differs;
    leave it unless separately measured.
648 | - | } | c54ba27Row pattern recognition patch (parse/analysis).
649 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
650 | 2980 | READ_BOOL_FIELD(isAbsorbable); | c54ba27Row pattern recognition patch (parse/analysis).
651 | - |  | c54ba27Row pattern recognition patch (parse/analysis).
652 | 2980 | READ_DONE(); | c54ba27Row pattern recognition patch (parse/analysis).
653 | - | } | c54ba27Row pattern recognition patch (parse/analysis).