| 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). |
UnreachableReason 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 fixKeep 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). |
UnreachableReason 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 fixReplace 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). |