← 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%)
LineHitsSourceCommit
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).