src/backend/executor/execExprInterp.c - Coverage Report

← Back to Overview

# src/backend/executor/execExprInterp.c

Coverage: 80/88 lines (90.9%)
Total Lines
88
modified
Covered
80
90.9%
Uncovered
8
9.1%
Keyboard navigation
ExecInterpExpr() lines 472-2311
Modified Lines Coverage: 8/8 lines (100.0%)

Line | Hits | Source | Commit
--- | --- | --- | ---
472 | - | ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) | -
473 | - | { | -
474 | - | ExprEvalStep *op; | -
475 | - | TupleTableSlot *resultslot; | -
476 | - | TupleTableSlot *innerslot; | -
477 | - | TupleTableSlot *outerslot; | -
478 | - | TupleTableSlot *scanslot; | -
479 | - | TupleTableSlot *oldslot; | -
480 | - | TupleTableSlot *newslot; | -
481 | - |  | -
482 | - | /* | -
483 | - | * This array has to be in the same order as enum ExprEvalOp. | -
484 | - | */ | -
485 | - | #if defined(EEO_USE_COMPUTED_GOTO) | -
486 | - | static const void *const dispatch_table[] = { | -
487 | - | &&CASE_EEOP_DONE_RETURN, | -
488 | - | &&CASE_EEOP_DONE_NO_RETURN, | -
489 | - | &&CASE_EEOP_INNER_FETCHSOME, | -
490 | - | &&CASE_EEOP_OUTER_FETCHSOME, | -
491 | - | &&CASE_EEOP_SCAN_FETCHSOME, | -
492 | - | &&CASE_EEOP_OLD_FETCHSOME, | -
493 | - | &&CASE_EEOP_NEW_FETCHSOME, | -
494 | - | &&CASE_EEOP_INNER_VAR, | -
495 | - | &&CASE_EEOP_OUTER_VAR, | -
496 | - | &&CASE_EEOP_SCAN_VAR, | -
497 | - | &&CASE_EEOP_OLD_VAR, | -
498 | - | &&CASE_EEOP_NEW_VAR, | -
499 | - | &&CASE_EEOP_INNER_SYSVAR, | -
500 | - | &&CASE_EEOP_OUTER_SYSVAR, | -
501 | - | &&CASE_EEOP_SCAN_SYSVAR, | -
502 | - | &&CASE_EEOP_OLD_SYSVAR, | -
503 | - | &&CASE_EEOP_NEW_SYSVAR, | -
504 | - | &&CASE_EEOP_WHOLEROW, | -
505 | - | &&CASE_EEOP_ASSIGN_INNER_VAR, | -
506 | - | &&CASE_EEOP_ASSIGN_OUTER_VAR, | -
507 | - | &&CASE_EEOP_ASSIGN_SCAN_VAR, | -
508 | - | &&CASE_EEOP_ASSIGN_OLD_VAR, | -
509 | - | &&CASE_EEOP_ASSIGN_NEW_VAR, | -
510 | - | &&CASE_EEOP_ASSIGN_TMP, | -
511 | - | &&CASE_EEOP_ASSIGN_TMP_MAKE_RO, | -
512 | - | &&CASE_EEOP_CONST, | -
513 | - | &&CASE_EEOP_FUNCEXPR, | -
514 | - | &&CASE_EEOP_FUNCEXPR_STRICT, | -
515 | - | &&CASE_EEOP_FUNCEXPR_STRICT_1, | -
516 | - | &&CASE_EEOP_FUNCEXPR_STRICT_2, | -
517 | - | &&CASE_EEOP_FUNCEXPR_FUSAGE, | -
518 | - | &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE, | -
519 | - | &&CASE_EEOP_BOOL_AND_STEP_FIRST, | -
520 | - | &&CASE_EEOP_BOOL_AND_STEP, | -
521 | - | &&CASE_EEOP_BOOL_AND_STEP_LAST, | -
522 | - | &&CASE_EEOP_BOOL_OR_STEP_FIRST, | -
523 | - | &&CASE_EEOP_BOOL_OR_STEP, | -
524 | - | &&CASE_EEOP_BOOL_OR_STEP_LAST, | -
525 | - | &&CASE_EEOP_BOOL_NOT_STEP, | -
526 | - | &&CASE_EEOP_QUAL, | -
527 | - | &&CASE_EEOP_JUMP, | -
528 | - | &&CASE_EEOP_JUMP_IF_NULL, | -
529 | - | &&CASE_EEOP_JUMP_IF_NOT_NULL, | -
530 | - | &&CASE_EEOP_JUMP_IF_NOT_TRUE, | -
531 | - | &&CASE_EEOP_NULLTEST_ISNULL, | -
532 | - | &&CASE_EEOP_NULLTEST_ISNOTNULL, | -
533 | - | &&CASE_EEOP_NULLTEST_ROWISNULL, | -
534 | - | &&CASE_EEOP_NULLTEST_ROWISNOTNULL, | -
535 | - | &&CASE_EEOP_BOOLTEST_IS_TRUE, | -
536 | - | &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE, | -
537 | - | &&CASE_EEOP_BOOLTEST_IS_FALSE, | -
538 | - | &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE, | -
539 | - | &&CASE_EEOP_PARAM_EXEC, | -
540 | - | &&CASE_EEOP_PARAM_EXTERN, | -
541 | - | &&CASE_EEOP_PARAM_CALLBACK, | -
542 | - | &&CASE_EEOP_PARAM_SET, | -
543 | - | &&CASE_EEOP_CASE_TESTVAL, | -
544 | - | &&CASE_EEOP_CASE_TESTVAL_EXT, | -
545 | - | &&CASE_EEOP_MAKE_READONLY, | -
546 | - | &&CASE_EEOP_IOCOERCE, | -
547 | - | &&CASE_EEOP_IOCOERCE_SAFE, | -
548 | - | &&CASE_EEOP_DISTINCT, | -
549 | - | &&CASE_EEOP_NOT_DISTINCT, | -
550 | - | &&CASE_EEOP_NULLIF, | -
551 | - | &&CASE_EEOP_SQLVALUEFUNCTION, | -
552 | - | &&CASE_EEOP_CURRENTOFEXPR, | -
553 | - | &&CASE_EEOP_NEXTVALUEEXPR, | -
554 | - | &&CASE_EEOP_RETURNINGEXPR, | -
555 | - | &&CASE_EEOP_ARRAYEXPR, | -
556 | - | &&CASE_EEOP_ARRAYCOERCE, | -
557 | - | &&CASE_EEOP_ROW, | -
558 | - | &&CASE_EEOP_ROWCOMPARE_STEP, | -
559 | - | &&CASE_EEOP_ROWCOMPARE_FINAL, | -
560 | - | &&CASE_EEOP_MINMAX, | -
561 | - | &&CASE_EEOP_FIELDSELECT, | -
562 | - | &&CASE_EEOP_FIELDSTORE_DEFORM, | -
563 | - | &&CASE_EEOP_FIELDSTORE_FORM, | -
564 | - | &&CASE_EEOP_SBSREF_SUBSCRIPTS, | -
565 | - | &&CASE_EEOP_SBSREF_OLD, | -
566 | - | &&CASE_EEOP_SBSREF_ASSIGN, | -
567 | - | &&CASE_EEOP_SBSREF_FETCH, | -
568 | - | &&CASE_EEOP_DOMAIN_TESTVAL, | -
569 | - | &&CASE_EEOP_DOMAIN_TESTVAL_EXT, | -
570 | - | &&CASE_EEOP_DOMAIN_NOTNULL, | -
571 | - | &&CASE_EEOP_DOMAIN_CHECK, | -
572 | - | &&CASE_EEOP_HASHDATUM_SET_INITVAL, | -
573 | - | &&CASE_EEOP_HASHDATUM_FIRST, | -
574 | - | &&CASE_EEOP_HASHDATUM_FIRST_STRICT, | -
575 | - | &&CASE_EEOP_HASHDATUM_NEXT32, | -
576 | - | &&CASE_EEOP_HASHDATUM_NEXT32_STRICT, | -
577 | - | &&CASE_EEOP_CONVERT_ROWTYPE, | -
578 | - | &&CASE_EEOP_SCALARARRAYOP, | -
579 | - | &&CASE_EEOP_HASHED_SCALARARRAYOP, | -
580 | - | &&CASE_EEOP_XMLEXPR, | -
581 | - | &&CASE_EEOP_JSON_CONSTRUCTOR, | -
582 | - | &&CASE_EEOP_IS_JSON, | -
583 | - | &&CASE_EEOP_JSONEXPR_PATH, | -
584 | - | &&CASE_EEOP_JSONEXPR_COERCION, | -
585 | - | &&CASE_EEOP_JSONEXPR_COERCION_FINISH, | -
586 | - | &&CASE_EEOP_AGGREF, | -
587 | - | &&CASE_EEOP_GROUPING_FUNC, | -
588 | - | &&CASE_EEOP_WINDOW_FUNC, | -
589 | - | &&CASE_EEOP_MERGE_SUPPORT_FUNC, | -
590 | - | &&CASE_EEOP_SUBPLAN, | -
591 | - | &&CASE_EEOP_RPR_NAV_SET, | 24cfb8dRow pattern recognition patch (executor and commands).
592 | - | &&CASE_EEOP_RPR_NAV_RESTORE, | 24cfb8dRow pattern recognition patch (executor and commands).
593 | - | &&CASE_EEOP_AGG_STRICT_DESERIALIZE, | -
594 | - | &&CASE_EEOP_AGG_DESERIALIZE, | -
595 | - | &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS, | -
596 | - | &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1, | -
597 | - | &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS, | -
598 | - | &&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK, | -
599 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL, | -
600 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL, | -
601 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL, | -
602 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF, | -
603 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF, | -
604 | - | &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF, | -
605 | - | &&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE, | -
606 | - | &&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI, | -
607 | - | &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM, | -
608 | - | &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE, | -
609 | - | &&CASE_EEOP_LAST | -
610 | - | }; | -
611 | - |  | -
612 | - | StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST + 1, | -
613 | - | "dispatch_table out of whack with ExprEvalOp"); | -
614 | - |  | -
615 | - | if (unlikely(state == NULL)) | -
616 | - | return PointerGetDatum(dispatch_table); | -
617 | - | #else | -
618 | - | Assert(state != NULL); | -
619 | - | #endif /* EEO_USE_COMPUTED_GOTO */ | -
620 | - |  | -
621 | - | /* setup state */ | -
622 | - | op = state->steps; | -
623 | - | resultslot = state->resultslot; | -
624 | - | innerslot = econtext->ecxt_innertuple; | -
625 | - | outerslot = econtext->ecxt_outertuple; | -
626 | - | scanslot = econtext->ecxt_scantuple; | -
627 | - | oldslot = econtext->ecxt_oldtuple; | -
628 | - | newslot = econtext->ecxt_newtuple; | -
629 | - |  | -
630 | - | #if defined(EEO_USE_COMPUTED_GOTO) | -
631 | - | EEO_DISPATCH(); | -
632 | - | #endif | -
633 | - |  | -
634 | - | EEO_SWITCH() | -
635 | - | { | -
636 | - | EEO_CASE(EEOP_DONE_RETURN) | -
637 | - | { | -
638 | - | *isnull = state->resnull; | -
639 | - | return state->resvalue; | -
640 | - | } | -
641 | - |  | -
642 | - | EEO_CASE(EEOP_DONE_NO_RETURN) | -
643 | - | { | -
644 | - | Assert(isnull == NULL); | -
645 | - | return (Datum) 0; | -
646 | - | } | -
647 | - |  | -
648 | - | EEO_CASE(EEOP_INNER_FETCHSOME) | -
649 | - | { | -
650 | - | CheckOpSlotCompatibility(op, innerslot); | -
651 | - |  | -
652 | - | slot_getsomeattrs(innerslot, op->d.fetch.last_var); | -
653 | - |  | -
654 | - | EEO_NEXT(); | -
655 | - | } | -
656 | - |  | -
657 | - | EEO_CASE(EEOP_OUTER_FETCHSOME) | -
658 | - | { | -
659 | - | CheckOpSlotCompatibility(op, outerslot); | -
660 | - |  | -
661 | - | slot_getsomeattrs(outerslot, op->d.fetch.last_var); | -
662 | - |  | -
663 | - | EEO_NEXT(); | -
664 | - | } | -
665 | - |  | -
666 | - | EEO_CASE(EEOP_SCAN_FETCHSOME) | -
667 | - | { | -
668 | - | CheckOpSlotCompatibility(op, scanslot); | -
669 | - |  | -
670 | - | slot_getsomeattrs(scanslot, op->d.fetch.last_var); | -
671 | - |  | -
672 | - | EEO_NEXT(); | -
673 | - | } | -
674 | - |  | -
675 | - | EEO_CASE(EEOP_OLD_FETCHSOME) | -
676 | - | { | -
677 | - | CheckOpSlotCompatibility(op, oldslot); | -
678 | - |  | -
679 | - | slot_getsomeattrs(oldslot, op->d.fetch.last_var); | -
680 | - |  | -
681 | - | EEO_NEXT(); | -
682 | - | } | -
683 | - |  | -
684 | - | EEO_CASE(EEOP_NEW_FETCHSOME) | -
685 | - | { | -
686 | - | CheckOpSlotCompatibility(op, newslot); | -
687 | - |  | -
688 | - | slot_getsomeattrs(newslot, op->d.fetch.last_var); | -
689 | - |  | -
690 | - | EEO_NEXT(); | -
691 | - | } | -
692 | - |  | -
693 | - | EEO_CASE(EEOP_INNER_VAR) | -
694 | - | { | -
695 | - | int attnum = op->d.var.attnum; | -
696 | - |  | -
697 | - | /* | -
698 | - | * Since we already extracted all referenced columns from the | -
699 | - | * tuple with a FETCHSOME step, we can just grab the value | -
700 | - | * directly out of the slot's decomposed-data arrays. But let's | -
701 | - | * have an Assert to check that that did happen. | -
702 | - | */ | -
703 | - | Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); | -
704 | - | *op->resvalue = innerslot->tts_values[attnum]; | -
705 | - | *op->resnull = innerslot->tts_isnull[attnum]; | -
706 | - |  | -
707 | - | EEO_NEXT(); | -
708 | - | } | -
709 | - |  | -
710 | - | EEO_CASE(EEOP_OUTER_VAR) | -
711 | - | { | -
712 | - | int attnum = op->d.var.attnum; | -
713 | - |  | -
714 | - | /* See EEOP_INNER_VAR comments */ | -
715 | - |  | -
716 | - | Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); | -
717 | - | *op->resvalue = outerslot->tts_values[attnum]; | -
718 | - | *op->resnull = outerslot->tts_isnull[attnum]; | -
719 | - |  | -
720 | - | EEO_NEXT(); | -
721 | - | } | -
722 | - |  | -
723 | - | EEO_CASE(EEOP_SCAN_VAR) | -
724 | - | { | -
725 | - | int attnum = op->d.var.attnum; | -
726 | - |  | -
727 | - | /* See EEOP_INNER_VAR comments */ | -
728 | - |  | -
729 | - | Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); | -
730 | - | *op->resvalue = scanslot->tts_values[attnum]; | -
731 | - | *op->resnull = scanslot->tts_isnull[attnum]; | -
732 | - |  | -
733 | - | EEO_NEXT(); | -
734 | - | } | -
735 | - |  | -
736 | - | EEO_CASE(EEOP_OLD_VAR) | -
737 | - | { | -
738 | - | int attnum = op->d.var.attnum; | -
739 | - |  | -
740 | - | /* See EEOP_INNER_VAR comments */ | -
741 | - |  | -
742 | - | Assert(attnum >= 0 && attnum < oldslot->tts_nvalid); | -
743 | - | *op->resvalue = oldslot->tts_values[attnum]; | -
744 | - | *op->resnull = oldslot->tts_isnull[attnum]; | -
745 | - |  | -
746 | - | EEO_NEXT(); | -
747 | - | } | -
748 | - |  | -
749 | - | EEO_CASE(EEOP_NEW_VAR) | -
750 | - | { | -
751 | - | int attnum = op->d.var.attnum; | -
752 | - |  | -
753 | - | /* See EEOP_INNER_VAR comments */ | -
754 | - |  | -
755 | - | Assert(attnum >= 0 && attnum < newslot->tts_nvalid); | -
756 | - | *op->resvalue = newslot->tts_values[attnum]; | -
757 | - | *op->resnull = newslot->tts_isnull[attnum]; | -
758 | - |  | -
759 | - | EEO_NEXT(); | -
760 | - | } | -
761 | - |  | -
762 | - | EEO_CASE(EEOP_INNER_SYSVAR) | -
763 | - | { | -
764 | - | ExecEvalSysVar(state, op, econtext, innerslot); | -
765 | - | EEO_NEXT(); | -
766 | - | } | -
767 | - |  | -
768 | - | EEO_CASE(EEOP_OUTER_SYSVAR) | -
769 | - | { | -
770 | - | ExecEvalSysVar(state, op, econtext, outerslot); | -
771 | - | EEO_NEXT(); | -
772 | - | } | -
773 | - |  | -
774 | - | EEO_CASE(EEOP_SCAN_SYSVAR) | -
775 | - | { | -
776 | - | ExecEvalSysVar(state, op, econtext, scanslot); | -
777 | - | EEO_NEXT(); | -
778 | - | } | -
779 | - |  | -
780 | - | EEO_CASE(EEOP_OLD_SYSVAR) | -
781 | - | { | -
782 | - | ExecEvalSysVar(state, op, econtext, oldslot); | -
783 | - | EEO_NEXT(); | -
784 | - | } | -
785 | - |  | -
786 | - | EEO_CASE(EEOP_NEW_SYSVAR) | -
787 | - | { | -
788 | - | ExecEvalSysVar(state, op, econtext, newslot); | -
789 | - | EEO_NEXT(); | -
790 | - | } | -
791 | - |  | -
792 | - | EEO_CASE(EEOP_WHOLEROW) | -
793 | - | { | -
794 | - | /* too complex for an inline implementation */ | -
795 | - | ExecEvalWholeRowVar(state, op, econtext); | -
796 | - |  | -
797 | - | EEO_NEXT(); | -
798 | - | } | -
799 | - |  | -
800 | - | EEO_CASE(EEOP_ASSIGN_INNER_VAR) | -
801 | - | { | -
802 | - | int resultnum = op->d.assign_var.resultnum; | -
803 | - | int attnum = op->d.assign_var.attnum; | -
804 | - |  | -
805 | - | /* | -
806 | - | * We do not need CheckVarSlotCompatibility here; that was taken | -
807 | - | * care of at compilation time. But see EEOP_INNER_VAR comments. | -
808 | - | */ | -
809 | - | Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); | -
810 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
811 | - | resultslot->tts_values[resultnum] = innerslot->tts_values[attnum]; | -
812 | - | resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum]; | -
813 | - |  | -
814 | - | EEO_NEXT(); | -
815 | - | } | -
816 | - |  | -
817 | - | EEO_CASE(EEOP_ASSIGN_OUTER_VAR) | -
818 | - | { | -
819 | - | int resultnum = op->d.assign_var.resultnum; | -
820 | - | int attnum = op->d.assign_var.attnum; | -
821 | - |  | -
822 | - | /* | -
823 | - | * We do not need CheckVarSlotCompatibility here; that was taken | -
824 | - | * care of at compilation time. But see EEOP_INNER_VAR comments. | -
825 | - | */ | -
826 | - | Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); | -
827 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
828 | - | resultslot->tts_values[resultnum] = outerslot->tts_values[attnum]; | -
829 | - | resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum]; | -
830 | - |  | -
831 | - | EEO_NEXT(); | -
832 | - | } | -
833 | - |  | -
834 | - | EEO_CASE(EEOP_ASSIGN_SCAN_VAR) | -
835 | - | { | -
836 | - | int resultnum = op->d.assign_var.resultnum; | -
837 | - | int attnum = op->d.assign_var.attnum; | -
838 | - |  | -
839 | - | /* | -
840 | - | * We do not need CheckVarSlotCompatibility here; that was taken | -
841 | - | * care of at compilation time. But see EEOP_INNER_VAR comments. | -
842 | - | */ | -
843 | - | Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); | -
844 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
845 | - | resultslot->tts_values[resultnum] = scanslot->tts_values[attnum]; | -
846 | - | resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum]; | -
847 | - |  | -
848 | - | EEO_NEXT(); | -
849 | - | } | -
850 | - |  | -
851 | - | EEO_CASE(EEOP_ASSIGN_OLD_VAR) | -
852 | - | { | -
853 | - | int resultnum = op->d.assign_var.resultnum; | -
854 | - | int attnum = op->d.assign_var.attnum; | -
855 | - |  | -
856 | - | /* | -
857 | - | * We do not need CheckVarSlotCompatibility here; that was taken | -
858 | - | * care of at compilation time. But see EEOP_INNER_VAR comments. | -
859 | - | */ | -
860 | - | Assert(attnum >= 0 && attnum < oldslot->tts_nvalid); | -
861 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
862 | - | resultslot->tts_values[resultnum] = oldslot->tts_values[attnum]; | -
863 | - | resultslot->tts_isnull[resultnum] = oldslot->tts_isnull[attnum]; | -
864 | - |  | -
865 | - | EEO_NEXT(); | -
866 | - | } | -
867 | - |  | -
868 | - | EEO_CASE(EEOP_ASSIGN_NEW_VAR) | -
869 | - | { | -
870 | - | int resultnum = op->d.assign_var.resultnum; | -
871 | - | int attnum = op->d.assign_var.attnum; | -
872 | - |  | -
873 | - | /* | -
874 | - | * We do not need CheckVarSlotCompatibility here; that was taken | -
875 | - | * care of at compilation time. But see EEOP_INNER_VAR comments. | -
876 | - | */ | -
877 | - | Assert(attnum >= 0 && attnum < newslot->tts_nvalid); | -
878 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
879 | - | resultslot->tts_values[resultnum] = newslot->tts_values[attnum]; | -
880 | - | resultslot->tts_isnull[resultnum] = newslot->tts_isnull[attnum]; | -
881 | - |  | -
882 | - | EEO_NEXT(); | -
883 | - | } | -
884 | - |  | -
885 | - | EEO_CASE(EEOP_ASSIGN_TMP) | -
886 | - | { | -
887 | - | int resultnum = op->d.assign_tmp.resultnum; | -
888 | - |  | -
889 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
890 | - | resultslot->tts_values[resultnum] = state->resvalue; | -
891 | - | resultslot->tts_isnull[resultnum] = state->resnull; | -
892 | - |  | -
893 | - | EEO_NEXT(); | -
894 | - | } | -
895 | - |  | -
896 | - | EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO) | -
897 | - | { | -
898 | - | int resultnum = op->d.assign_tmp.resultnum; | -
899 | - |  | -
900 | - | Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); | -
901 | - | resultslot->tts_isnull[resultnum] = state->resnull; | -
902 | - | if (!resultslot->tts_isnull[resultnum]) | -
903 | - | resultslot->tts_values[resultnum] = | -
904 | - | MakeExpandedObjectReadOnlyInternal(state->resvalue); | -
905 | - | else | -
906 | - | resultslot->tts_values[resultnum] = state->resvalue; | -
907 | - |  | -
908 | - | EEO_NEXT(); | -
909 | - | } | -
910 | - |  | -
911 | - | EEO_CASE(EEOP_CONST) | -
912 | - | { | -
913 | - | *op->resnull = op->d.constval.isnull; | -
914 | - | *op->resvalue = op->d.constval.value; | -
915 | - |  | -
916 | - | EEO_NEXT(); | -
917 | - | } | -
918 | - |  | -
919 | - | /* | -
920 | - | * Function-call implementations. Arguments have previously been | -
921 | - | * evaluated directly into fcinfo->args. | -
922 | - | * | -
923 | - | * As both STRICT checks and function-usage are noticeable performance | -
924 | - | * wise, and function calls are a very hot-path (they also back | -
925 | - | * operators!), it's worth having so many separate opcodes. | -
926 | - | * | -
927 | - | * Note: the reason for using a temporary variable "d", here and in | -
928 | - | * other places, is that some compilers think "*op->resvalue = f();" | -
929 | - | * requires them to evaluate op->resvalue into a register before | -
930 | - | * calling f(), just in case f() is able to modify op->resvalue | -
931 | - | * somehow. The extra line of code can save a useless register spill | -
932 | - | * and reload across the function call. | -
933 | - | */ | -
934 | - | EEO_CASE(EEOP_FUNCEXPR) | -
935 | - | { | -
936 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
937 | - | Datum d; | -
938 | - |  | -
939 | - | fcinfo->isnull = false; | -
940 | - | d = op->d.func.fn_addr(fcinfo); | -
941 | - | *op->resvalue = d; | -
942 | - | *op->resnull = fcinfo->isnull; | -
943 | - |  | -
944 | - | EEO_NEXT(); | -
945 | - | } | -
946 | - |  | -
947 | - | /* strict function call with more than two arguments */ | -
948 | - | EEO_CASE(EEOP_FUNCEXPR_STRICT) | -
949 | - | { | -
950 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
951 | - | NullableDatum *args = fcinfo->args; | -
952 | - | int nargs = op->d.func.nargs; | -
953 | - | Datum d; | -
954 | - |  | -
955 | - | Assert(nargs > 2); | -
956 | - |  | -
957 | - | /* strict function, so check for NULL args */ | -
958 | - | for (int argno = 0; argno < nargs; argno++) | -
959 | - | { | -
960 | - | if (args[argno].isnull) | -
961 | - | { | -
962 | - | *op->resnull = true; | -
963 | - | goto strictfail; | -
964 | - | } | -
965 | - | } | -
966 | - | fcinfo->isnull = false; | -
967 | - | d = op->d.func.fn_addr(fcinfo); | -
968 | - | *op->resvalue = d; | -
969 | - | *op->resnull = fcinfo->isnull; | -
970 | - |  | -
971 | - | strictfail: | -
972 | - | EEO_NEXT(); | -
973 | - | } | -
974 | - |  | -
975 | - | /* strict function call with one argument */ | -
976 | - | EEO_CASE(EEOP_FUNCEXPR_STRICT_1) | -
977 | - | { | -
978 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
979 | - | NullableDatum *args = fcinfo->args; | -
980 | - |  | -
981 | - | Assert(op->d.func.nargs == 1); | -
982 | - |  | -
983 | - | /* strict function, so check for NULL args */ | -
984 | - | if (args[0].isnull) | -
985 | - | *op->resnull = true; | -
986 | - | else | -
987 | - | { | -
988 | - | Datum d; | -
989 | - |  | -
990 | - | fcinfo->isnull = false; | -
991 | - | d = op->d.func.fn_addr(fcinfo); | -
992 | - | *op->resvalue = d; | -
993 | - | *op->resnull = fcinfo->isnull; | -
994 | - | } | -
995 | - |  | -
996 | - | EEO_NEXT(); | -
997 | - | } | -
998 | - |  | -
999 | - | /* strict function call with two arguments */ | -
1000 | - | EEO_CASE(EEOP_FUNCEXPR_STRICT_2) | -
1001 | - | { | -
1002 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
1003 | - | NullableDatum *args = fcinfo->args; | -
1004 | - |  | -
1005 | - | Assert(op->d.func.nargs == 2); | -
1006 | - |  | -
1007 | - | /* strict function, so check for NULL args */ | -
1008 | - | if (args[0].isnull || args[1].isnull) | -
1009 | - | *op->resnull = true; | -
1010 | - | else | -
1011 | - | { | -
1012 | - | Datum d; | -
1013 | - |  | -
1014 | - | fcinfo->isnull = false; | -
1015 | - | d = op->d.func.fn_addr(fcinfo); | -
1016 | - | *op->resvalue = d; | -
1017 | - | *op->resnull = fcinfo->isnull; | -
1018 | - | } | -
1019 | - |  | -
1020 | - | EEO_NEXT(); | -
1021 | - | } | -
1022 | - |  | -
1023 | - | EEO_CASE(EEOP_FUNCEXPR_FUSAGE) | -
1024 | - | { | -
1025 | - | /* not common enough to inline */ | -
1026 | - | ExecEvalFuncExprFusage(state, op, econtext); | -
1027 | - |  | -
1028 | - | EEO_NEXT(); | -
1029 | - | } | -
1030 | - |  | -
1031 | - | EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE) | -
1032 | - | { | -
1033 | - | /* not common enough to inline */ | -
1034 | - | ExecEvalFuncExprStrictFusage(state, op, econtext); | -
1035 | - |  | -
1036 | - | EEO_NEXT(); | -
1037 | - | } | -
1038 | - |  | -
1039 | - | /* | -
1040 | - | * If any of its clauses is FALSE, an AND's result is FALSE regardless | -
1041 | - | * of the states of the rest of the clauses, so we can stop evaluating | -
1042 | - | * and return FALSE immediately. If none are FALSE and one or more is | -
1043 | - | * NULL, we return NULL; otherwise we return TRUE. This makes sense | -
1044 | - | * when you interpret NULL as "don't know": perhaps one of the "don't | -
1045 | - | * knows" would have been FALSE if we'd known its value. Only when | -
1046 | - | * all the inputs are known to be TRUE can we state confidently that | -
1047 | - | * the AND's result is TRUE. | -
1048 | - | */ | -
1049 | - | EEO_CASE(EEOP_BOOL_AND_STEP_FIRST) | -
1050 | - | { | -
1051 | - | *op->d.boolexpr.anynull = false; | -
1052 | - |  | -
1053 | - | /* | -
1054 | - | * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the | -
1055 | - | * same as EEOP_BOOL_AND_STEP - so fall through to that. | -
1056 | - | */ | -
1057 | - |  | -
1058 | - | /* FALL THROUGH */ | -
1059 | - | } | -
1060 | - |  | -
1061 | - | EEO_CASE(EEOP_BOOL_AND_STEP) | -
1062 | - | { | -
1063 | - | if (*op->resnull) | -
1064 | - | { | -
1065 | - | *op->d.boolexpr.anynull = true; | -
1066 | - | } | -
1067 | - | else if (!DatumGetBool(*op->resvalue)) | -
1068 | - | { | -
1069 | - | /* result is already set to FALSE, need not change it */ | -
1070 | - | /* bail out early */ | -
1071 | - | EEO_JUMP(op->d.boolexpr.jumpdone); | -
1072 | - | } | -
1073 | - |  | -
1074 | - | EEO_NEXT(); | -
1075 | - | } | -
1076 | - |  | -
1077 | - | EEO_CASE(EEOP_BOOL_AND_STEP_LAST) | -
1078 | - | { | -
1079 | - | if (*op->resnull) | -
1080 | - | { | -
1081 | - | /* result is already set to NULL, need not change it */ | -
1082 | - | } | -
1083 | - | else if (!DatumGetBool(*op->resvalue)) | -
1084 | - | { | -
1085 | - | /* result is already set to FALSE, need not change it */ | -
1086 | - |  | -
1087 | - | /* | -
1088 | - | * No point jumping early to jumpdone - would be same target | -
1089 | - | * (as this is the last argument to the AND expression), | -
1090 | - | * except more expensive. | -
1091 | - | */ | -
1092 | - | } | -
1093 | - | else if (*op->d.boolexpr.anynull) | -
1094 | - | { | -
1095 | - | *op->resvalue = (Datum) 0; | -
1096 | - | *op->resnull = true; | -
1097 | - | } | -
1098 | - | else | -
1099 | - | { | -
1100 | - | /* result is already set to TRUE, need not change it */ | -
1101 | - | } | -
1102 | - |  | -
1103 | - | EEO_NEXT(); | -
1104 | - | } | -
1105 | - |  | -
1106 | - | /* | -
1107 | - | * If any of its clauses is TRUE, an OR's result is TRUE regardless of | -
1108 | - | * the states of the rest of the clauses, so we can stop evaluating | -
1109 | - | * and return TRUE immediately. If none are TRUE and one or more is | -
1110 | - | * NULL, we return NULL; otherwise we return FALSE. This makes sense | -
1111 | - | * when you interpret NULL as "don't know": perhaps one of the "don't | -
1112 | - | * knows" would have been TRUE if we'd known its value. Only when all | -
1113 | - | * the inputs are known to be FALSE can we state confidently that the | -
1114 | - | * OR's result is FALSE. | -
1115 | - | */ | -
1116 | - | EEO_CASE(EEOP_BOOL_OR_STEP_FIRST) | -
1117 | - | { | -
1118 | - | *op->d.boolexpr.anynull = false; | -
1119 | - |  | -
1120 | - | /* | -
1121 | - | * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same | -
1122 | - | * as EEOP_BOOL_OR_STEP - so fall through to that. | -
1123 | - | */ | -
1124 | - |  | -
1125 | - | /* FALL THROUGH */ | -
1126 | - | } | -
1127 | - |  | -
1128 | - | EEO_CASE(EEOP_BOOL_OR_STEP) | -
1129 | - | { | -
1130 | - | if (*op->resnull) | -
1131 | - | { | -
1132 | - | *op->d.boolexpr.anynull = true; | -
1133 | - | } | -
1134 | - | else if (DatumGetBool(*op->resvalue)) | -
1135 | - | { | -
1136 | - | /* result is already set to TRUE, need not change it */ | -
1137 | - | /* bail out early */ | -
1138 | - | EEO_JUMP(op->d.boolexpr.jumpdone); | -
1139 | - | } | -
1140 | - |  | -
1141 | - | EEO_NEXT(); | -
1142 | - | } | -
1143 | - |  | -
1144 | - | EEO_CASE(EEOP_BOOL_OR_STEP_LAST) | -
1145 | - | { | -
1146 | - | if (*op->resnull) | -
1147 | - | { | -
1148 | - | /* result is already set to NULL, need not change it */ | -
1149 | - | } | -
1150 | - | else if (DatumGetBool(*op->resvalue)) | -
1151 | - | { | -
1152 | - | /* result is already set to TRUE, need not change it */ | -
1153 | - |  | -
1154 | - | /* | -
1155 | - | * No point jumping to jumpdone - would be same target (as | -
1156 | - | * this is the last argument to the AND expression), except | -
1157 | - | * more expensive. | -
1158 | - | */ | -
1159 | - | } | -
1160 | - | else if (*op->d.boolexpr.anynull) | -
1161 | - | { | -
1162 | - | *op->resvalue = (Datum) 0; | -
1163 | - | *op->resnull = true; | -
1164 | - | } | -
1165 | - | else | -
1166 | - | { | -
1167 | - | /* result is already set to FALSE, need not change it */ | -
1168 | - | } | -
1169 | - |  | -
1170 | - | EEO_NEXT(); | -
1171 | - | } | -
1172 | - |  | -
1173 | - | EEO_CASE(EEOP_BOOL_NOT_STEP) | -
1174 | - | { | -
1175 | - | /* | -
1176 | - | * Evaluation of 'not' is simple... if expr is false, then return | -
1177 | - | * 'true' and vice versa. It's safe to do this even on a | -
1178 | - | * nominally null value, so we ignore resnull; that means that | -
1179 | - | * NULL in produces NULL out, which is what we want. | -
1180 | - | */ | -
1181 | - | *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); | -
1182 | - |  | -
1183 | - | EEO_NEXT(); | -
1184 | - | } | -
1185 | - |  | -
1186 | - | EEO_CASE(EEOP_QUAL) | -
1187 | - | { | -
1188 | - | /* simplified version of BOOL_AND_STEP for use by ExecQual() */ | -
1189 | - |  | -
1190 | - | /* If argument (also result) is false or null ... */ | -
1191 | - | if (*op->resnull || | -
1192 | - | !DatumGetBool(*op->resvalue)) | -
1193 | - | { | -
1194 | - | /* ... bail out early, returning FALSE */ | -
1195 | - | *op->resnull = false; | -
1196 | - | *op->resvalue = BoolGetDatum(false); | -
1197 | - | EEO_JUMP(op->d.qualexpr.jumpdone); | -
1198 | - | } | -
1199 | - |  | -
1200 | - | /* | -
1201 | - | * Otherwise, leave the TRUE value in place, in case this is the | -
1202 | - | * last qual. Then, TRUE is the correct answer. | -
1203 | - | */ | -
1204 | - |  | -
1205 | - | EEO_NEXT(); | -
1206 | - | } | -
1207 | - |  | -
1208 | - | EEO_CASE(EEOP_JUMP) | -
1209 | - | { | -
1210 | - | /* Unconditionally jump to target step */ | -
1211 | - | EEO_JUMP(op->d.jump.jumpdone); | -
1212 | - | } | -
1213 | - |  | -
1214 | - | EEO_CASE(EEOP_JUMP_IF_NULL) | -
1215 | - | { | -
1216 | - | /* Transfer control if current result is null */ | -
1217 | - | if (*op->resnull) | -
1218 | - | EEO_JUMP(op->d.jump.jumpdone); | -
1219 | - |  | -
1220 | - | EEO_NEXT(); | -
1221 | - | } | -
1222 | - |  | -
1223 | - | EEO_CASE(EEOP_JUMP_IF_NOT_NULL) | -
1224 | - | { | -
1225 | - | /* Transfer control if current result is non-null */ | -
1226 | - | if (!*op->resnull) | -
1227 | - | EEO_JUMP(op->d.jump.jumpdone); | -
1228 | - |  | -
1229 | - | EEO_NEXT(); | -
1230 | - | } | -
1231 | - |  | -
1232 | - | EEO_CASE(EEOP_JUMP_IF_NOT_TRUE) | -
1233 | - | { | -
1234 | - | /* Transfer control if current result is null or false */ | -
1235 | - | if (*op->resnull || !DatumGetBool(*op->resvalue)) | -
1236 | - | EEO_JUMP(op->d.jump.jumpdone); | -
1237 | - |  | -
1238 | - | EEO_NEXT(); | -
1239 | - | } | -
1240 | - |  | -
1241 | - | EEO_CASE(EEOP_NULLTEST_ISNULL) | -
1242 | - | { | -
1243 | - | *op->resvalue = BoolGetDatum(*op->resnull); | -
1244 | - | *op->resnull = false; | -
1245 | - |  | -
1246 | - | EEO_NEXT(); | -
1247 | - | } | -
1248 | - |  | -
1249 | - | EEO_CASE(EEOP_NULLTEST_ISNOTNULL) | -
1250 | - | { | -
1251 | - | *op->resvalue = BoolGetDatum(!*op->resnull); | -
1252 | - | *op->resnull = false; | -
1253 | - |  | -
1254 | - | EEO_NEXT(); | -
1255 | - | } | -
1256 | - |  | -
1257 | - | EEO_CASE(EEOP_NULLTEST_ROWISNULL) | -
1258 | - | { | -
1259 | - | /* out of line implementation: too large */ | -
1260 | - | ExecEvalRowNull(state, op, econtext); | -
1261 | - |  | -
1262 | - | EEO_NEXT(); | -
1263 | - | } | -
1264 | - |  | -
1265 | - | EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL) | -
1266 | - | { | -
1267 | - | /* out of line implementation: too large */ | -
1268 | - | ExecEvalRowNotNull(state, op, econtext); | -
1269 | - |  | -
1270 | - | EEO_NEXT(); | -
1271 | - | } | -
1272 | - |  | -
1273 | - | /* BooleanTest implementations for all booltesttypes */ | -
1274 | - |  | -
1275 | - | EEO_CASE(EEOP_BOOLTEST_IS_TRUE) | -
1276 | - | { | -
1277 | - | if (*op->resnull) | -
1278 | - | { | -
1279 | - | *op->resvalue = BoolGetDatum(false); | -
1280 | - | *op->resnull = false; | -
1281 | - | } | -
1282 | - | /* else, input value is the correct output as well */ | -
1283 | - |  | -
1284 | - | EEO_NEXT(); | -
1285 | - | } | -
1286 | - |  | -
1287 | - | EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE) | -
1288 | - | { | -
1289 | - | if (*op->resnull) | -
1290 | - | { | -
1291 | - | *op->resvalue = BoolGetDatum(true); | -
1292 | - | *op->resnull = false; | -
1293 | - | } | -
1294 | - | else | -
1295 | - | *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); | -
1296 | - |  | -
1297 | - | EEO_NEXT(); | -
1298 | - | } | -
1299 | - |  | -
1300 | - | EEO_CASE(EEOP_BOOLTEST_IS_FALSE) | -
1301 | - | { | -
1302 | - | if (*op->resnull) | -
1303 | - | { | -
1304 | - | *op->resvalue = BoolGetDatum(false); | -
1305 | - | *op->resnull = false; | -
1306 | - | } | -
1307 | - | else | -
1308 | - | *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); | -
1309 | - |  | -
1310 | - | EEO_NEXT(); | -
1311 | - | } | -
1312 | - |  | -
1313 | - | EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE) | -
1314 | - | { | -
1315 | - | if (*op->resnull) | -
1316 | - | { | -
1317 | - | *op->resvalue = BoolGetDatum(true); | -
1318 | - | *op->resnull = false; | -
1319 | - | } | -
1320 | - | /* else, input value is the correct output as well */ | -
1321 | - |  | -
1322 | - | EEO_NEXT(); | -
1323 | - | } | -
1324 | - |  | -
1325 | - | EEO_CASE(EEOP_PARAM_EXEC) | -
1326 | - | { | -
1327 | - | /* out of line implementation: too large */ | -
1328 | - | ExecEvalParamExec(state, op, econtext); | -
1329 | - |  | -
1330 | - | EEO_NEXT(); | -
1331 | - | } | -
1332 | - |  | -
1333 | - | EEO_CASE(EEOP_PARAM_EXTERN) | -
1334 | - | { | -
1335 | - | /* out of line implementation: too large */ | -
1336 | - | ExecEvalParamExtern(state, op, econtext); | -
1337 | - | EEO_NEXT(); | -
1338 | - | } | -
1339 | - |  | -
1340 | - | EEO_CASE(EEOP_PARAM_CALLBACK) | -
1341 | - | { | -
1342 | - | /* allow an extension module to supply a PARAM_EXTERN value */ | -
1343 | - | op->d.cparam.paramfunc(state, op, econtext); | -
1344 | - | EEO_NEXT(); | -
1345 | - | } | -
1346 | - |  | -
1347 | - | EEO_CASE(EEOP_PARAM_SET) | -
1348 | - | { | -
1349 | - | /* out of line, unlikely to matter performance-wise */ | -
1350 | - | ExecEvalParamSet(state, op, econtext); | -
1351 | - | EEO_NEXT(); | -
1352 | - | } | -
1353 | - |  | -
1354 | - | EEO_CASE(EEOP_CASE_TESTVAL) | -
1355 | - | { | -
1356 | - | *op->resvalue = *op->d.casetest.value; | -
1357 | - | *op->resnull = *op->d.casetest.isnull; | -
1358 | - |  | -
1359 | - | EEO_NEXT(); | -
1360 | - | } | -
1361 | - |  | -
1362 | - | EEO_CASE(EEOP_CASE_TESTVAL_EXT) | -
1363 | - | { | -
1364 | - | *op->resvalue = econtext->caseValue_datum; | -
1365 | - | *op->resnull = econtext->caseValue_isNull; | -
1366 | - |  | -
1367 | - | EEO_NEXT(); | -
1368 | - | } | -
1369 | - |  | -
1370 | - | EEO_CASE(EEOP_MAKE_READONLY) | -
1371 | - | { | -
1372 | - | /* | -
1373 | - | * Force a varlena value that might be read multiple times to R/O | -
1374 | - | */ | -
1375 | - | if (!*op->d.make_readonly.isnull) | -
1376 | - | *op->resvalue = | -
1377 | - | MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value); | -
1378 | - | *op->resnull = *op->d.make_readonly.isnull; | -
1379 | - |  | -
1380 | - | EEO_NEXT(); | -
1381 | - | } | -
1382 | - |  | -
1383 | - | EEO_CASE(EEOP_IOCOERCE) | -
1384 | - | { | -
1385 | - | /* | -
1386 | - | * Evaluate a CoerceViaIO node. This can be quite a hot path, so | -
1387 | - | * inline as much work as possible. The source value is in our | -
1388 | - | * result variable. | -
1389 | - | * | -
1390 | - | * Also look at ExecEvalCoerceViaIOSafe() if you change anything | -
1391 | - | * here. | -
1392 | - | */ | -
1393 | - | char *str; | -
1394 | - |  | -
1395 | - | /* call output function (similar to OutputFunctionCall) */ | -
1396 | - | if (*op->resnull) | -
1397 | - | { | -
1398 | - | /* output functions are not called on nulls */ | -
1399 | - | str = NULL; | -
1400 | - | } | -
1401 | - | else | -
1402 | - | { | -
1403 | - | FunctionCallInfo fcinfo_out; | -
1404 | - |  | -
1405 | - | fcinfo_out = op->d.iocoerce.fcinfo_data_out; | -
1406 | - | fcinfo_out->args[0].value = *op->resvalue; | -
1407 | - | fcinfo_out->args[0].isnull = false; | -
1408 | - |  | -
1409 | - | fcinfo_out->isnull = false; | -
1410 | - | str = DatumGetCString(FunctionCallInvoke(fcinfo_out)); | -
1411 | - |  | -
1412 | - | /* OutputFunctionCall assumes result isn't null */ | -
1413 | - | Assert(!fcinfo_out->isnull); | -
1414 | - | } | -
1415 | - |  | -
1416 | - | /* call input function (similar to InputFunctionCall) */ | -
1417 | - | if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL) | -
1418 | - | { | -
1419 | - | FunctionCallInfo fcinfo_in; | -
1420 | - | Datum d; | -
1421 | - |  | -
1422 | - | fcinfo_in = op->d.iocoerce.fcinfo_data_in; | -
1423 | - | fcinfo_in->args[0].value = PointerGetDatum(str); | -
1424 | - | fcinfo_in->args[0].isnull = *op->resnull; | -
1425 | - | /* second and third arguments are already set up */ | -
1426 | - |  | -
1427 | - | fcinfo_in->isnull = false; | -
1428 | - | d = FunctionCallInvoke(fcinfo_in); | -
1429 | - | *op->resvalue = d; | -
1430 | - |  | -
1431 | - | /* Should get null result if and only if str is NULL */ | -
1432 | - | if (str == NULL) | -
1433 | - | { | -
1434 | - | Assert(*op->resnull); | -
1435 | - | Assert(fcinfo_in->isnull); | -
1436 | - | } | -
1437 | - | else | -
1438 | - | { | -
1439 | - | Assert(!*op->resnull); | -
1440 | - | Assert(!fcinfo_in->isnull); | -
1441 | - | } | -
1442 | - | } | -
1443 | - |  | -
1444 | - | EEO_NEXT(); | -
1445 | - | } | -
1446 | - |  | -
1447 | - | EEO_CASE(EEOP_IOCOERCE_SAFE) | -
1448 | - | { | -
1449 | - | ExecEvalCoerceViaIOSafe(state, op); | -
1450 | - | EEO_NEXT(); | -
1451 | - | } | -
1452 | - |  | -
1453 | - | EEO_CASE(EEOP_DISTINCT) | -
1454 | - | { | -
1455 | - | /* | -
1456 | - | * IS DISTINCT FROM must evaluate arguments (already done into | -
1457 | - | * fcinfo->args) to determine whether they are NULL; if either is | -
1458 | - | * NULL then the result is determined. If neither is NULL, then | -
1459 | - | * proceed to evaluate the comparison function, which is just the | -
1460 | - | * type's standard equality operator. We need not care whether | -
1461 | - | * that function is strict. Because the handling of nulls is | -
1462 | - | * different, we can't just reuse EEOP_FUNCEXPR. | -
1463 | - | */ | -
1464 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
1465 | - |  | -
1466 | - | /* check function arguments for NULLness */ | -
1467 | - | if (fcinfo->args[0].isnull && fcinfo->args[1].isnull) | -
1468 | - | { | -
1469 | - | /* Both NULL? Then is not distinct... */ | -
1470 | - | *op->resvalue = BoolGetDatum(false); | -
1471 | - | *op->resnull = false; | -
1472 | - | } | -
1473 | - | else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull) | -
1474 | - | { | -
1475 | - | /* Only one is NULL? Then is distinct... */ | -
1476 | - | *op->resvalue = BoolGetDatum(true); | -
1477 | - | *op->resnull = false; | -
1478 | - | } | -
1479 | - | else | -
1480 | - | { | -
1481 | - | /* Neither null, so apply the equality function */ | -
1482 | - | Datum eqresult; | -
1483 | - |  | -
1484 | - | fcinfo->isnull = false; | -
1485 | - | eqresult = op->d.func.fn_addr(fcinfo); | -
1486 | - | /* Must invert result of "="; safe to do even if null */ | -
1487 | - | *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult)); | -
1488 | - | *op->resnull = fcinfo->isnull; | -
1489 | - | } | -
1490 | - |  | -
1491 | - | EEO_NEXT(); | -
1492 | - | } | -
1493 | - |  | -
1494 | - | /* see EEOP_DISTINCT for comments, this is just inverted */ | -
1495 | - | EEO_CASE(EEOP_NOT_DISTINCT) | -
1496 | - | { | -
1497 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
1498 | - |  | -
1499 | - | if (fcinfo->args[0].isnull && fcinfo->args[1].isnull) | -
1500 | - | { | -
1501 | - | *op->resvalue = BoolGetDatum(true); | -
1502 | - | *op->resnull = false; | -
1503 | - | } | -
1504 | - | else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull) | -
1505 | - | { | -
1506 | - | *op->resvalue = BoolGetDatum(false); | -
1507 | - | *op->resnull = false; | -
1508 | - | } | -
1509 | - | else | -
1510 | - | { | -
1511 | - | Datum eqresult; | -
1512 | - |  | -
1513 | - | fcinfo->isnull = false; | -
1514 | - | eqresult = op->d.func.fn_addr(fcinfo); | -
1515 | - | *op->resvalue = eqresult; | -
1516 | - | *op->resnull = fcinfo->isnull; | -
1517 | - | } | -
1518 | - |  | -
1519 | - | EEO_NEXT(); | -
1520 | - | } | -
1521 | - |  | -
1522 | - | EEO_CASE(EEOP_NULLIF) | -
1523 | - | { | -
1524 | - | /* | -
1525 | - | * The arguments are already evaluated into fcinfo->args. | -
1526 | - | */ | -
1527 | - | FunctionCallInfo fcinfo = op->d.func.fcinfo_data; | -
1528 | - | Datum save_arg0 = fcinfo->args[0].value; | -
1529 | - |  | -
1530 | - | /* if either argument is NULL they can't be equal */ | -
1531 | - | if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull) | -
1532 | - | { | -
1533 | - | Datum result; | -
1534 | - |  | -
1535 | - | /* | -
1536 | - | * If first argument is of varlena type, it might be an | -
1537 | - | * expanded datum. We need to ensure that the value passed to | -
1538 | - | * the comparison function is a read-only pointer. However, | -
1539 | - | * if we end by returning the first argument, that will be the | -
1540 | - | * original read-write pointer if it was read-write. | -
1541 | - | */ | -
1542 | - | if (op->d.func.make_ro) | -
1543 | - | fcinfo->args[0].value = | -
1544 | - | MakeExpandedObjectReadOnlyInternal(save_arg0); | -
1545 | - |  | -
1546 | - | fcinfo->isnull = false; | -
1547 | - | result = op->d.func.fn_addr(fcinfo); | -
1548 | - |  | -
1549 | - | /* if the arguments are equal return null */ | -
1550 | - | if (!fcinfo->isnull && DatumGetBool(result)) | -
1551 | - | { | -
1552 | - | *op->resvalue = (Datum) 0; | -
1553 | - | *op->resnull = true; | -
1554 | - |  | -
1555 | - | EEO_NEXT(); | -
1556 | - | } | -
1557 | - | } | -
1558 | - |  | -
1559 | - | /* Arguments aren't equal, so return the first one */ | -
1560 | - | *op->resvalue = save_arg0; | -
1561 | - | *op->resnull = fcinfo->args[0].isnull; | -
1562 | - |  | -
1563 | - | EEO_NEXT(); | -
1564 | - | } | -
1565 | - |  | -
1566 | - | EEO_CASE(EEOP_SQLVALUEFUNCTION) | -
1567 | - | { | -
1568 | - | /* | -
1569 | - | * Doesn't seem worthwhile to have an inline implementation | -
1570 | - | * efficiency-wise. | -
1571 | - | */ | -
1572 | - | ExecEvalSQLValueFunction(state, op); | -
1573 | - |  | -
1574 | - | EEO_NEXT(); | -
1575 | - | } | -
1576 | - |  | -
1577 | - | EEO_CASE(EEOP_CURRENTOFEXPR) | -
1578 | - | { | -
1579 | - | /* error invocation uses space, and shouldn't ever occur */ | -
1580 | - | ExecEvalCurrentOfExpr(state, op); | -
1581 | - |  | -
1582 | - | EEO_NEXT(); | -
1583 | - | } | -
1584 | - |  | -
1585 | - | EEO_CASE(EEOP_NEXTVALUEEXPR) | -
1586 | - | { | -
1587 | - | /* | -
1588 | - | * Doesn't seem worthwhile to have an inline implementation | -
1589 | - | * efficiency-wise. | -
1590 | - | */ | -
1591 | - | ExecEvalNextValueExpr(state, op); | -
1592 | - |  | -
1593 | - | EEO_NEXT(); | -
1594 | - | } | -
1595 | - |  | -
1596 | - | EEO_CASE(EEOP_RETURNINGEXPR) | -
1597 | - | { | -
1598 | - | /* | -
1599 | - | * The next op actually evaluates the expression. If the OLD/NEW | -
1600 | - | * row doesn't exist, skip that and return NULL. | -
1601 | - | */ | -
1602 | - | if (state->flags & op->d.returningexpr.nullflag) | -
1603 | - | { | -
1604 | - | *op->resvalue = (Datum) 0; | -
1605 | - | *op->resnull = true; | -
1606 | - |  | -
1607 | - | EEO_JUMP(op->d.returningexpr.jumpdone); | -
1608 | - | } | -
1609 | - |  | -
1610 | - | EEO_NEXT(); | -
1611 | - | } | -
1612 | - |  | -
1613 | - | EEO_CASE(EEOP_ARRAYEXPR) | -
1614 | - | { | -
1615 | - | /* too complex for an inline implementation */ | -
1616 | - | ExecEvalArrayExpr(state, op); | -
1617 | - |  | -
1618 | - | EEO_NEXT(); | -
1619 | - | } | -
1620 | - |  | -
1621 | - | EEO_CASE(EEOP_ARRAYCOERCE) | -
1622 | - | { | -
1623 | - | /* too complex for an inline implementation */ | -
1624 | - | ExecEvalArrayCoerce(state, op, econtext); | -
1625 | - |  | -
1626 | - | EEO_NEXT(); | -
1627 | - | } | -
1628 | - |  | -
1629 | - | EEO_CASE(EEOP_ROW) | -
1630 | - | { | -
1631 | - | /* too complex for an inline implementation */ | -
1632 | - | ExecEvalRow(state, op); | -
1633 | - |  | -
1634 | - | EEO_NEXT(); | -
1635 | - | } | -
1636 | - |  | -
1637 | - | EEO_CASE(EEOP_ROWCOMPARE_STEP) | -
1638 | - | { | -
1639 | - | FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data; | -
1640 | - | Datum d; | -
1641 | - |  | -
1642 | - | /* force NULL result if strict fn and NULL input */ | -
1643 | - | if (op->d.rowcompare_step.finfo->fn_strict && | -
1644 | - | (fcinfo->args[0].isnull || fcinfo->args[1].isnull)) | -
1645 | - | { | -
1646 | - | *op->resnull = true; | -
1647 | - | EEO_JUMP(op->d.rowcompare_step.jumpnull); | -
1648 | - | } | -
1649 | - |  | -
1650 | - | /* Apply comparison function */ | -
1651 | - | fcinfo->isnull = false; | -
1652 | - | d = op->d.rowcompare_step.fn_addr(fcinfo); | -
1653 | - | *op->resvalue = d; | -
1654 | - |  | -
1655 | - | /* force NULL result if NULL function result */ | -
1656 | - | if (fcinfo->isnull) | -
1657 | - | { | -
1658 | - | *op->resnull = true; | -
1659 | - | EEO_JUMP(op->d.rowcompare_step.jumpnull); | -
1660 | - | } | -
1661 | - | *op->resnull = false; | -
1662 | - |  | -
1663 | - | /* If unequal, no need to compare remaining columns */ | -
1664 | - | if (DatumGetInt32(*op->resvalue) != 0) | -
1665 | - | { | -
1666 | - | EEO_JUMP(op->d.rowcompare_step.jumpdone); | -
1667 | - | } | -
1668 | - |  | -
1669 | - | EEO_NEXT(); | -
1670 | - | } | -
1671 | - |  | -
1672 | - | EEO_CASE(EEOP_ROWCOMPARE_FINAL) | -
1673 | - | { | -
1674 | - | int32 cmpresult = DatumGetInt32(*op->resvalue); | -
1675 | - | CompareType cmptype = op->d.rowcompare_final.cmptype; | -
1676 | - |  | -
1677 | - | *op->resnull = false; | -
1678 | - | switch (cmptype) | -
1679 | - | { | -
1680 | - | /* EQ and NE cases aren't allowed here */ | -
1681 | - | case COMPARE_LT: | -
1682 | - | *op->resvalue = BoolGetDatum(cmpresult < 0); | -
1683 | - | break; | -
1684 | - | case COMPARE_LE: | -
1685 | - | *op->resvalue = BoolGetDatum(cmpresult <= 0); | -
1686 | - | break; | -
1687 | - | case COMPARE_GE: | -
1688 | - | *op->resvalue = BoolGetDatum(cmpresult >= 0); | -
1689 | - | break; | -
1690 | - | case COMPARE_GT: | -
1691 | - | *op->resvalue = BoolGetDatum(cmpresult > 0); | -
1692 | - | break; | -
1693 | - | default: | -
1694 | - | Assert(false); | -
1695 | - | break; | -
1696 | - | } | -
1697 | - |  | -
1698 | - | EEO_NEXT(); | -
1699 | - | } | -
1700 | - |  | -
1701 | - | EEO_CASE(EEOP_MINMAX) | -
1702 | - | { | -
1703 | - | /* too complex for an inline implementation */ | -
1704 | - | ExecEvalMinMax(state, op); | -
1705 | - |  | -
1706 | - | EEO_NEXT(); | -
1707 | - | } | -
1708 | - |  | -
1709 | - | EEO_CASE(EEOP_FIELDSELECT) | -
1710 | - | { | -
1711 | - | /* too complex for an inline implementation */ | -
1712 | - | ExecEvalFieldSelect(state, op, econtext); | -
1713 | - |  | -
1714 | - | EEO_NEXT(); | -
1715 | - | } | -
1716 | - |  | -
1717 | - | EEO_CASE(EEOP_FIELDSTORE_DEFORM) | -
1718 | - | { | -
1719 | - | /* too complex for an inline implementation */ | -
1720 | - | ExecEvalFieldStoreDeForm(state, op, econtext); | -
1721 | - |  | -
1722 | - | EEO_NEXT(); | -
1723 | - | } | -
1724 | - |  | -
1725 | - | EEO_CASE(EEOP_FIELDSTORE_FORM) | -
1726 | - | { | -
1727 | - | /* too complex for an inline implementation */ | -
1728 | - | ExecEvalFieldStoreForm(state, op, econtext); | -
1729 | - |  | -
1730 | - | EEO_NEXT(); | -
1731 | - | } | -
1732 | - |  | -
1733 | - | EEO_CASE(EEOP_SBSREF_SUBSCRIPTS) | -
1734 | - | { | -
1735 | - | /* Precheck SubscriptingRef subscript(s) */ | -
1736 | - | if (op->d.sbsref_subscript.subscriptfunc(state, op, econtext)) | -
1737 | - | { | -
1738 | - | EEO_NEXT(); | -
1739 | - | } | -
1740 | - | else | -
1741 | - | { | -
1742 | - | /* Subscript is null, short-circuit SubscriptingRef to NULL */ | -
1743 | - | EEO_JUMP(op->d.sbsref_subscript.jumpdone); | -
1744 | - | } | -
1745 | - | } | -
1746 | - |  | -
1747 | - | EEO_CASE(EEOP_SBSREF_OLD) | -
1748 | - | EEO_CASE(EEOP_SBSREF_ASSIGN) | -
1749 | - | EEO_CASE(EEOP_SBSREF_FETCH) | -
1750 | - | { | -
1751 | - | /* Perform a SubscriptingRef fetch or assignment */ | -
1752 | - | op->d.sbsref.subscriptfunc(state, op, econtext); | -
1753 | - |  | -
1754 | - | EEO_NEXT(); | -
1755 | - | } | -
1756 | - |  | -
1757 | - | EEO_CASE(EEOP_CONVERT_ROWTYPE) | -
1758 | - | { | -
1759 | - | /* too complex for an inline implementation */ | -
1760 | - | ExecEvalConvertRowtype(state, op, econtext); | -
1761 | - |  | -
1762 | - | EEO_NEXT(); | -
1763 | - | } | -
1764 | - |  | -
1765 | - | EEO_CASE(EEOP_SCALARARRAYOP) | -
1766 | - | { | -
1767 | - | /* too complex for an inline implementation */ | -
1768 | - | ExecEvalScalarArrayOp(state, op); | -
1769 | - |  | -
1770 | - | EEO_NEXT(); | -
1771 | - | } | -
1772 | - |  | -
1773 | - | EEO_CASE(EEOP_HASHED_SCALARARRAYOP) | -
1774 | - | { | -
1775 | - | /* too complex for an inline implementation */ | -
1776 | - | ExecEvalHashedScalarArrayOp(state, op, econtext); | -
1777 | - |  | -
1778 | - | EEO_NEXT(); | -
1779 | - | } | -
1780 | - |  | -
1781 | - | EEO_CASE(EEOP_DOMAIN_TESTVAL) | -
1782 | - | { | -
1783 | - | *op->resvalue = *op->d.casetest.value; | -
1784 | - | *op->resnull = *op->d.casetest.isnull; | -
1785 | - |  | -
1786 | - | EEO_NEXT(); | -
1787 | - | } | -
1788 | - |  | -
1789 | - | EEO_CASE(EEOP_DOMAIN_TESTVAL_EXT) | -
1790 | - | { | -
1791 | - | *op->resvalue = econtext->domainValue_datum; | -
1792 | - | *op->resnull = econtext->domainValue_isNull; | -
1793 | - |  | -
1794 | - | EEO_NEXT(); | -
1795 | - | } | -
1796 | - |  | -
1797 | - | EEO_CASE(EEOP_DOMAIN_NOTNULL) | -
1798 | - | { | -
1799 | - | /* too complex for an inline implementation */ | -
1800 | - | ExecEvalConstraintNotNull(state, op); | -
1801 | - |  | -
1802 | - | EEO_NEXT(); | -
1803 | - | } | -
1804 | - |  | -
1805 | - | EEO_CASE(EEOP_DOMAIN_CHECK) | -
1806 | - | { | -
1807 | - | /* too complex for an inline implementation */ | -
1808 | - | ExecEvalConstraintCheck(state, op); | -
1809 | - |  | -
1810 | - | EEO_NEXT(); | -
1811 | - | } | -
1812 | - |  | -
1813 | - | EEO_CASE(EEOP_HASHDATUM_SET_INITVAL) | -
1814 | - | { | -
1815 | - | *op->resvalue = op->d.hashdatum_initvalue.init_value; | -
1816 | - | *op->resnull = false; | -
1817 | - |  | -
1818 | - | EEO_NEXT(); | -
1819 | - | } | -
1820 | - |  | -
1821 | - | EEO_CASE(EEOP_HASHDATUM_FIRST) | -
1822 | - | { | -
1823 | - | FunctionCallInfo fcinfo = op->d.hashdatum.fcinfo_data; | -
1824 | - |  | -
1825 | - | /* | -
1826 | - | * Save the Datum on non-null inputs, otherwise store 0 so that | -
1827 | - | * subsequent NEXT32 operations combine with an initialized value. | -
1828 | - | */ | -
1829 | - | if (!fcinfo->args[0].isnull) | -
1830 | - | *op->resvalue = op->d.hashdatum.fn_addr(fcinfo); | -
1831 | - | else | -
1832 | - | *op->resvalue = (Datum) 0; | -
1833 | - |  | -
1834 | - | *op->resnull = false; | -
1835 | - |  | -
1836 | - | EEO_NEXT(); | -
1837 | - | } | -
1838 | - |  | -
1839 | - | EEO_CASE(EEOP_HASHDATUM_FIRST_STRICT) | -
1840 | - | { | -
1841 | - | FunctionCallInfo fcinfo = op->d.hashdatum.fcinfo_data; | -
1842 | - |  | -
1843 | - | if (fcinfo->args[0].isnull) | -
1844 | - | { | -
1845 | - | /* | -
1846 | - | * With strict we have the expression return NULL instead of | -
1847 | - | * ignoring NULL input values. We've nothing more to do after | -
1848 | - | * finding a NULL. | -
1849 | - | */ | -
1850 | - | *op->resnull = true; | -
1851 | - | *op->resvalue = (Datum) 0; | -
1852 | - | EEO_JUMP(op->d.hashdatum.jumpdone); | -
1853 | - | } | -
1854 | - |  | -
1855 | - | /* execute the hash function and save the resulting value */ | -
1856 | - | *op->resvalue = op->d.hashdatum.fn_addr(fcinfo); | -
1857 | - | *op->resnull = false; | -
1858 | - |  | -
1859 | - | EEO_NEXT(); | -
1860 | - | } | -
1861 | - |  | -
1862 | - | EEO_CASE(EEOP_HASHDATUM_NEXT32) | -
1863 | - | { | -
1864 | - | FunctionCallInfo fcinfo = op->d.hashdatum.fcinfo_data; | -
1865 | - | uint32 existinghash; | -
1866 | - |  | -
1867 | - | existinghash = DatumGetUInt32(op->d.hashdatum.iresult->value); | -
1868 | - | /* combine successive hash values by rotating */ | -
1869 | - | existinghash = pg_rotate_left32(existinghash, 1); | -
1870 | - |  | -
1871 | - | /* leave the hash value alone on NULL inputs */ | -
1872 | - | if (!fcinfo->args[0].isnull) | -
1873 | - | { | -
1874 | - | uint32 hashvalue; | -
1875 | - |  | -
1876 | - | /* execute hash func and combine with previous hash value */ | -
1877 | - | hashvalue = DatumGetUInt32(op->d.hashdatum.fn_addr(fcinfo)); | -
1878 | - | existinghash = existinghash ^ hashvalue; | -
1879 | - | } | -
1880 | - |  | -
1881 | - | *op->resvalue = UInt32GetDatum(existinghash); | -
1882 | - | *op->resnull = false; | -
1883 | - |  | -
1884 | - | EEO_NEXT(); | -
1885 | - | } | -
1886 | - |  | -
1887 | - | EEO_CASE(EEOP_HASHDATUM_NEXT32_STRICT) | -
1888 | - | { | -
1889 | - | FunctionCallInfo fcinfo = op->d.hashdatum.fcinfo_data; | -
1890 | - |  | -
1891 | - | if (fcinfo->args[0].isnull) | -
1892 | - | { | -
1893 | - | /* | -
1894 | - | * With strict we have the expression return NULL instead of | -
1895 | - | * ignoring NULL input values. We've nothing more to do after | -
1896 | - | * finding a NULL. | -
1897 | - | */ | -
1898 | - | *op->resnull = true; | -
1899 | - | *op->resvalue = (Datum) 0; | -
1900 | - | EEO_JUMP(op->d.hashdatum.jumpdone); | -
1901 | - | } | -
1902 | - | else | -
1903 | - | { | -
1904 | - | uint32 existinghash; | -
1905 | - | uint32 hashvalue; | -
1906 | - |  | -
1907 | - | existinghash = DatumGetUInt32(op->d.hashdatum.iresult->value); | -
1908 | - | /* combine successive hash values by rotating */ | -
1909 | - | existinghash = pg_rotate_left32(existinghash, 1); | -
1910 | - |  | -
1911 | - | /* execute hash func and combine with previous hash value */ | -
1912 | - | hashvalue = DatumGetUInt32(op->d.hashdatum.fn_addr(fcinfo)); | -
1913 | - | *op->resvalue = UInt32GetDatum(existinghash ^ hashvalue); | -
1914 | - | *op->resnull = false; | -
1915 | - | } | -
1916 | - |  | -
1917 | - | EEO_NEXT(); | -
1918 | - | } | -
1919 | - |  | -
1920 | - | EEO_CASE(EEOP_XMLEXPR) | -
1921 | - | { | -
1922 | - | /* too complex for an inline implementation */ | -
1923 | - | ExecEvalXmlExpr(state, op); | -
1924 | - |  | -
1925 | - | EEO_NEXT(); | -
1926 | - | } | -
1927 | - |  | -
1928 | - | EEO_CASE(EEOP_JSON_CONSTRUCTOR) | -
1929 | - | { | -
1930 | - | /* too complex for an inline implementation */ | -
1931 | - | ExecEvalJsonConstructor(state, op, econtext); | -
1932 | - | EEO_NEXT(); | -
1933 | - | } | -
1934 | - |  | -
1935 | - | EEO_CASE(EEOP_IS_JSON) | -
1936 | - | { | -
1937 | - | /* too complex for an inline implementation */ | -
1938 | - | ExecEvalJsonIsPredicate(state, op); | -
1939 | - |  | -
1940 | - | EEO_NEXT(); | -
1941 | - | } | -
1942 | - |  | -
1943 | - | EEO_CASE(EEOP_JSONEXPR_PATH) | -
1944 | - | { | -
1945 | - | /* too complex for an inline implementation */ | -
1946 | - | EEO_JUMP(ExecEvalJsonExprPath(state, op, econtext)); | -
1947 | - | } | -
1948 | - |  | -
1949 | - | EEO_CASE(EEOP_JSONEXPR_COERCION) | -
1950 | - | { | -
1951 | - | /* too complex for an inline implementation */ | -
1952 | - | ExecEvalJsonCoercion(state, op, econtext); | -
1953 | - |  | -
1954 | - | EEO_NEXT(); | -
1955 | - | } | -
1956 | - |  | -
1957 | - | EEO_CASE(EEOP_JSONEXPR_COERCION_FINISH) | -
1958 | - | { | -
1959 | - | /* too complex for an inline implementation */ | -
1960 | - | ExecEvalJsonCoercionFinish(state, op); | -
1961 | - |  | -
1962 | - | EEO_NEXT(); | -
1963 | - | } | -
1964 | - |  | -
1965 | - | EEO_CASE(EEOP_AGGREF) | -
1966 | - | { | -
1967 | - | /* | -
1968 | - | * Returns a Datum whose value is the precomputed aggregate value | -
1969 | - | * found in the given expression context. | -
1970 | - | */ | -
1971 | - | int aggno = op->d.aggref.aggno; | -
1972 | - |  | -
1973 | - | Assert(econtext->ecxt_aggvalues != NULL); | -
1974 | - |  | -
1975 | - | *op->resvalue = econtext->ecxt_aggvalues[aggno]; | -
1976 | - | *op->resnull = econtext->ecxt_aggnulls[aggno]; | -
1977 | - |  | -
1978 | - | EEO_NEXT(); | -
1979 | - | } | -
1980 | - |  | -
1981 | - | EEO_CASE(EEOP_GROUPING_FUNC) | -
1982 | - | { | -
1983 | - | /* too complex/uncommon for an inline implementation */ | -
1984 | - | ExecEvalGroupingFunc(state, op); | -
1985 | - |  | -
1986 | - | EEO_NEXT(); | -
1987 | - | } | -
1988 | - |  | -
1989 | - | EEO_CASE(EEOP_WINDOW_FUNC) | -
1990 | - | { | -
1991 | - | /* | -
1992 | - | * Like Aggref, just return a precomputed value from the econtext. | -
1993 | - | */ | -
1994 | - | WindowFuncExprState *wfunc = op->d.window_func.wfstate; | -
1995 | - |  | -
1996 | - | Assert(econtext->ecxt_aggvalues != NULL); | -
1997 | - |  | -
1998 | - | *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno]; | -
1999 | - | *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno]; | -
2000 | - |  | -
2001 | - | EEO_NEXT(); | -
2002 | - | } | -
2003 | - |  | -
2004 | - | EEO_CASE(EEOP_MERGE_SUPPORT_FUNC) | -
2005 | - | { | -
2006 | - | /* too complex/uncommon for an inline implementation */ | -
2007 | - | ExecEvalMergeSupportFunc(state, op, econtext); | -
2008 | - |  | -
2009 | - | EEO_NEXT(); | -
2010 | - | } | -
2011 | - |  | -
2012 | - | EEO_CASE(EEOP_SUBPLAN) | -
2013 | - | { | -
2014 | - | /* too complex for an inline implementation */ | -
2015 | - | ExecEvalSubPlan(state, op, econtext); | -
2016 | - |  | -
2017 | - | EEO_NEXT(); | -
2018 | - | } | -
2019 | - |  | -
2020 | - | /* RPR navigation: swap slot to target row */ | 24cfb8dRow pattern recognition patch (executor and commands).
2021 | 176028 | EEO_CASE(EEOP_RPR_NAV_SET) | 24cfb8dRow pattern recognition patch (executor and commands).
2022 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
2023 | 176028 | ExecEvalRPRNavSet(state, op, econtext); | 24cfb8dRow pattern recognition patch (executor and commands).
2024 | 175968 | outerslot = econtext->ecxt_outertuple; | 24cfb8dRow pattern recognition patch (executor and commands).
2025 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
2026 | 175968 | EEO_NEXT(); | 24cfb8dRow pattern recognition patch (executor and commands).
2027 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
2028 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
2029 | - | /* RPR navigation: restore slot to original row */ | 24cfb8dRow pattern recognition patch (executor and commands).
2030 | 175968 | EEO_CASE(EEOP_RPR_NAV_RESTORE) | 24cfb8dRow pattern recognition patch (executor and commands).
2031 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
2032 | 175968 | ExecEvalRPRNavRestore(state, op, econtext); | 24cfb8dRow pattern recognition patch (executor and commands).
2033 | 175968 | outerslot = econtext->ecxt_outertuple; | 24cfb8dRow pattern recognition patch (executor and commands).
2034 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
2035 | 175968 | EEO_NEXT(); | 24cfb8dRow pattern recognition patch (executor and commands).
2036 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
2037 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
2038 | - | /* evaluate a strict aggregate deserialization function */ | -
2039 | - | EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE) | -
2040 | - | { | -
2041 | - | /* Don't call a strict deserialization function with NULL input */ | -
2042 | - | if (op->d.agg_deserialize.fcinfo_data->args[0].isnull) | -
2043 | - | EEO_JUMP(op->d.agg_deserialize.jumpnull); | -
2044 | - |  | -
2045 | - | /* fallthrough */ | -
2046 | - | } | -
2047 | - |  | -
2048 | - | /* evaluate aggregate deserialization function (non-strict portion) */ | -
2049 | - | EEO_CASE(EEOP_AGG_DESERIALIZE) | -
2050 | - | { | -
2051 | - | FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data; | -
2052 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2053 | - | MemoryContext oldContext; | -
2054 | - |  | -
2055 | - | /* | -
2056 | - | * We run the deserialization functions in per-input-tuple memory | -
2057 | - | * context. | -
2058 | - | */ | -
2059 | - | oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); | -
2060 | - | fcinfo->isnull = false; | -
2061 | - | *op->resvalue = FunctionCallInvoke(fcinfo); | -
2062 | - | *op->resnull = fcinfo->isnull; | -
2063 | - | MemoryContextSwitchTo(oldContext); | -
2064 | - |  | -
2065 | - | EEO_NEXT(); | -
2066 | - | } | -
2067 | - |  | -
2068 | - | /* | -
2069 | - | * Check that a strict aggregate transition / combination function's | -
2070 | - | * input is not NULL. | -
2071 | - | */ | -
2072 | - |  | -
2073 | - | /* when checking more than one argument */ | -
2074 | - | EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS) | -
2075 | - | { | -
2076 | - | NullableDatum *args = op->d.agg_strict_input_check.args; | -
2077 | - | int nargs = op->d.agg_strict_input_check.nargs; | -
2078 | - |  | -
2079 | - | Assert(nargs > 1); | -
2080 | - |  | -
2081 | - | for (int argno = 0; argno < nargs; argno++) | -
2082 | - | { | -
2083 | - | if (args[argno].isnull) | -
2084 | - | EEO_JUMP(op->d.agg_strict_input_check.jumpnull); | -
2085 | - | } | -
2086 | - | EEO_NEXT(); | -
2087 | - | } | -
2088 | - |  | -
2089 | - | /* special case for just one argument */ | -
2090 | - | EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS_1) | -
2091 | - | { | -
2092 | - | NullableDatum *args = op->d.agg_strict_input_check.args; | -
2093 | - | PG_USED_FOR_ASSERTS_ONLY int nargs = op->d.agg_strict_input_check.nargs; | -
2094 | - |  | -
2095 | - | Assert(nargs == 1); | -
2096 | - |  | -
2097 | - | if (args[0].isnull) | -
2098 | - | EEO_JUMP(op->d.agg_strict_input_check.jumpnull); | -
2099 | - | EEO_NEXT(); | -
2100 | - | } | -
2101 | - |  | -
2102 | - | EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS) | -
2103 | - | { | -
2104 | - | bool *nulls = op->d.agg_strict_input_check.nulls; | -
2105 | - | int nargs = op->d.agg_strict_input_check.nargs; | -
2106 | - |  | -
2107 | - | for (int argno = 0; argno < nargs; argno++) | -
2108 | - | { | -
2109 | - | if (nulls[argno]) | -
2110 | - | EEO_JUMP(op->d.agg_strict_input_check.jumpnull); | -
2111 | - | } | -
2112 | - | EEO_NEXT(); | -
2113 | - | } | -
2114 | - |  | -
2115 | - | /* | -
2116 | - | * Check for a NULL pointer to the per-group states. | -
2117 | - | */ | -
2118 | - |  | -
2119 | - | EEO_CASE(EEOP_AGG_PLAIN_PERGROUP_NULLCHECK) | -
2120 | - | { | -
2121 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2122 | - | AggStatePerGroup pergroup_allaggs = | -
2123 | - | aggstate->all_pergroups[op->d.agg_plain_pergroup_nullcheck.setoff]; | -
2124 | - |  | -
2125 | - | if (pergroup_allaggs == NULL) | -
2126 | - | EEO_JUMP(op->d.agg_plain_pergroup_nullcheck.jumpnull); | -
2127 | - |  | -
2128 | - | EEO_NEXT(); | -
2129 | - | } | -
2130 | - |  | -
2131 | - | /* | -
2132 | - | * Different types of aggregate transition functions are implemented | -
2133 | - | * as different types of steps, to avoid incurring unnecessary | -
2134 | - | * overhead. There's a step type for each valid combination of having | -
2135 | - | * a by value / by reference transition type, [not] needing to the | -
2136 | - | * initialize the transition value for the first row in a group from | -
2137 | - | * input, and [not] strict transition function. | -
2138 | - | * | -
2139 | - | * Could optimize further by splitting off by-reference for | -
2140 | - | * fixed-length types, but currently that doesn't seem worth it. | -
2141 | - | */ | -
2142 | - |  | -
2143 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL) | -
2144 | - | { | -
2145 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2146 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2147 | - | AggStatePerGroup pergroup = | -
2148 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2149 | - |  | -
2150 | - | Assert(pertrans->transtypeByVal); | -
2151 | - |  | -
2152 | - | if (pergroup->noTransValue) | -
2153 | - | { | -
2154 | - | /* If transValue has not yet been initialized, do so now. */ | -
2155 | - | ExecAggInitGroup(aggstate, pertrans, pergroup, | -
2156 | - | op->d.agg_trans.aggcontext); | -
2157 | - | /* copied trans value from input, done this round */ | -
2158 | - | } | -
2159 | - | else if (likely(!pergroup->transValueIsNull)) | -
2160 | - | { | -
2161 | - | /* invoke transition function, unless prevented by strictness */ | -
2162 | - | ExecAggPlainTransByVal(aggstate, pertrans, pergroup, | -
2163 | - | op->d.agg_trans.aggcontext, | -
2164 | - | op->d.agg_trans.setno); | -
2165 | - | } | -
2166 | - |  | -
2167 | - | EEO_NEXT(); | -
2168 | - | } | -
2169 | - |  | -
2170 | - | /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ | -
2171 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL) | -
2172 | - | { | -
2173 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2174 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2175 | - | AggStatePerGroup pergroup = | -
2176 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2177 | - |  | -
2178 | - | Assert(pertrans->transtypeByVal); | -
2179 | - |  | -
2180 | - | if (likely(!pergroup->transValueIsNull)) | -
2181 | - | ExecAggPlainTransByVal(aggstate, pertrans, pergroup, | -
2182 | - | op->d.agg_trans.aggcontext, | -
2183 | - | op->d.agg_trans.setno); | -
2184 | - |  | -
2185 | - | EEO_NEXT(); | -
2186 | - | } | -
2187 | - |  | -
2188 | - | /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ | -
2189 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL) | -
2190 | - | { | -
2191 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2192 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2193 | - | AggStatePerGroup pergroup = | -
2194 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2195 | - |  | -
2196 | - | Assert(pertrans->transtypeByVal); | -
2197 | - |  | -
2198 | - | ExecAggPlainTransByVal(aggstate, pertrans, pergroup, | -
2199 | - | op->d.agg_trans.aggcontext, | -
2200 | - | op->d.agg_trans.setno); | -
2201 | - |  | -
2202 | - | EEO_NEXT(); | -
2203 | - | } | -
2204 | - |  | -
2205 | - | /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ | -
2206 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF) | -
2207 | - | { | -
2208 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2209 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2210 | - | AggStatePerGroup pergroup = | -
2211 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2212 | - |  | -
2213 | - | Assert(!pertrans->transtypeByVal); | -
2214 | - |  | -
2215 | - | if (pergroup->noTransValue) | -
2216 | - | ExecAggInitGroup(aggstate, pertrans, pergroup, | -
2217 | - | op->d.agg_trans.aggcontext); | -
2218 | - | else if (likely(!pergroup->transValueIsNull)) | -
2219 | - | ExecAggPlainTransByRef(aggstate, pertrans, pergroup, | -
2220 | - | op->d.agg_trans.aggcontext, | -
2221 | - | op->d.agg_trans.setno); | -
2222 | - |  | -
2223 | - | EEO_NEXT(); | -
2224 | - | } | -
2225 | - |  | -
2226 | - | /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ | -
2227 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYREF) | -
2228 | - | { | -
2229 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2230 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2231 | - | AggStatePerGroup pergroup = | -
2232 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2233 | - |  | -
2234 | - | Assert(!pertrans->transtypeByVal); | -
2235 | - |  | -
2236 | - | if (likely(!pergroup->transValueIsNull)) | -
2237 | - | ExecAggPlainTransByRef(aggstate, pertrans, pergroup, | -
2238 | - | op->d.agg_trans.aggcontext, | -
2239 | - | op->d.agg_trans.setno); | -
2240 | - | EEO_NEXT(); | -
2241 | - | } | -
2242 | - |  | -
2243 | - | /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ | -
2244 | - | EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYREF) | -
2245 | - | { | -
2246 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2247 | - | AggStatePerTrans pertrans = op->d.agg_trans.pertrans; | -
2248 | - | AggStatePerGroup pergroup = | -
2249 | - | &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno]; | -
2250 | - |  | -
2251 | - | Assert(!pertrans->transtypeByVal); | -
2252 | - |  | -
2253 | - | ExecAggPlainTransByRef(aggstate, pertrans, pergroup, | -
2254 | - | op->d.agg_trans.aggcontext, | -
2255 | - | op->d.agg_trans.setno); | -
2256 | - |  | -
2257 | - | EEO_NEXT(); | -
2258 | - | } | -
2259 | - |  | -
2260 | - | EEO_CASE(EEOP_AGG_PRESORTED_DISTINCT_SINGLE) | -
2261 | - | { | -
2262 | - | AggStatePerTrans pertrans = op->d.agg_presorted_distinctcheck.pertrans; | -
2263 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2264 | - |  | -
2265 | - | if (ExecEvalPreOrderedDistinctSingle(aggstate, pertrans)) | -
2266 | - | EEO_NEXT(); | -
2267 | - | else | -
2268 | - | EEO_JUMP(op->d.agg_presorted_distinctcheck.jumpdistinct); | -
2269 | - | } | -
2270 | - |  | -
2271 | - | EEO_CASE(EEOP_AGG_PRESORTED_DISTINCT_MULTI) | -
2272 | - | { | -
2273 | - | AggState *aggstate = castNode(AggState, state->parent); | -
2274 | - | AggStatePerTrans pertrans = op->d.agg_presorted_distinctcheck.pertrans; | -
2275 | - |  | -
2276 | - | if (ExecEvalPreOrderedDistinctMulti(aggstate, pertrans)) | -
2277 | - | EEO_NEXT(); | -
2278 | - | else | -
2279 | - | EEO_JUMP(op->d.agg_presorted_distinctcheck.jumpdistinct); | -
2280 | - | } | -
2281 | - |  | -
2282 | - | /* process single-column ordered aggregate datum */ | -
2283 | - | EEO_CASE(EEOP_AGG_ORDERED_TRANS_DATUM) | -
2284 | - | { | -
2285 | - | /* too complex for an inline implementation */ | -
2286 | - | ExecEvalAggOrderedTransDatum(state, op, econtext); | -
2287 | - |  | -
2288 | - | EEO_NEXT(); | -
2289 | - | } | -
2290 | - |  | -
2291 | - | /* process multi-column ordered aggregate tuple */ | -
2292 | - | EEO_CASE(EEOP_AGG_ORDERED_TRANS_TUPLE) | -
2293 | - | { | -
2294 | - | /* too complex for an inline implementation */ | -
2295 | - | ExecEvalAggOrderedTransTuple(state, op, econtext); | -
2296 | - |  | -
2297 | - | EEO_NEXT(); | -
2298 | - | } | -
2299 | - |  | -
2300 | - | EEO_CASE(EEOP_LAST) | -
2301 | - | { | -
2302 | - | /* unreachable */ | -
2303 | - | Assert(false); | -
2304 | - | goto out_error; | -
2305 | - | } | -
2306 | - | } | -
2307 | - |  | -
2308 | - | out_error: | -
2309 | - | pg_unreachable(); | -
2310 | - | return (Datum) 0; | -
2311 | - | } | -

rpr_nav_get_compound_offset() lines 6021-6040
Modified Lines Coverage: 9/9 lines (100.0%)

Line | Hits | Source | Commit
--- | --- | --- | ---
6021 | 9744 | rpr_nav_get_compound_offset(ExprEvalStep *op) | 24cfb8dRow pattern recognition patch (executor and commands).
6022 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6023 | 9744 | int64 val; | 24cfb8dRow pattern recognition patch (executor and commands).
6024 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6025 | 9744 | Assert(op->d.rpr_nav.offset_value != NULL); | 24cfb8dRow pattern recognition patch (executor and commands).
6026 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6027 | 9744 | if (op->d.rpr_nav.offset_isnull[1]) | 24cfb8dRow pattern recognition patch (executor and commands).
6028 | 4 | ereport(ERROR, | 24cfb8dRow pattern recognition patch (executor and commands).
6029 | - | errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | 24cfb8dRow pattern recognition patch (executor and commands).
6030 | - | errmsg("row pattern navigation offset must not be null")); | 24cfb8dRow pattern recognition patch (executor and commands).
6031 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6032 | 9740 | val = DatumGetInt64(op->d.rpr_nav.offset_value[1]); | 24cfb8dRow pattern recognition patch (executor and commands).
6033 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6034 | 9740 | if (val < 0) | 24cfb8dRow pattern recognition patch (executor and commands).
6035 | 4 | ereport(ERROR, | 24cfb8dRow pattern recognition patch (executor and commands).
6036 | - | errcode(ERRCODE_INVALID_PARAMETER_VALUE), | 24cfb8dRow pattern recognition patch (executor and commands).
6037 | - | errmsg("row pattern navigation offset must not be negative")); | 24cfb8dRow pattern recognition patch (executor and commands).
6038 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6039 | 9736 | return val; | 24cfb8dRow pattern recognition patch (executor and commands).
6040 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).

ExecEvalRPRNavSet() lines 6053-6220
Modified Lines Coverage: 52/60 lines (86.7%)

Line | Hits | Source | Commit
--- | --- | --- | ---
6053 | 984024 | ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) | 24cfb8dRow pattern recognition patch (executor and commands).
6054 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6055 | 984024 | WindowAggState *winstate = op->d.rpr_nav.winstate; | 24cfb8dRow pattern recognition patch (executor and commands).
6056 | 984024 | int64 offset; | 24cfb8dRow pattern recognition patch (executor and commands).
6057 | 984024 | int64 target_pos; | 24cfb8dRow pattern recognition patch (executor and commands).
6058 | 984024 | TupleTableSlot *target_slot; | 24cfb8dRow pattern recognition patch (executor and commands).
6059 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6060 | - | /* Save current slot for later restore */ | 24cfb8dRow pattern recognition patch (executor and commands).
6061 | 984024 | winstate->nav_saved_outertuple = econtext->ecxt_outertuple; | 24cfb8dRow pattern recognition patch (executor and commands).
6062 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6063 | - | /* | 24cfb8dRow pattern recognition patch (executor and commands).
6064 | - | * Determine the inner offset. NULL or negative offsets are errors per | 24cfb8dRow pattern recognition patch (executor and commands).
6065 | - | * the SQL standard. | 24cfb8dRow pattern recognition patch (executor and commands).
6066 | - | * | 24cfb8dRow pattern recognition patch (executor and commands).
6067 | - | * Default offset when offset_arg is NULL: PREV/NEXT: 1 (standard 5.6.2) | 24cfb8dRow pattern recognition patch (executor and commands).
6068 | - | * FIRST/LAST and compound: 0 for inner, 1 for outer | 24cfb8dRow pattern recognition patch (executor and commands).
6069 | - | */ | 24cfb8dRow pattern recognition patch (executor and commands).
6070 | 984024 | if (op->d.rpr_nav.offset_value != NULL) | 24cfb8dRow pattern recognition patch (executor and commands).
6071 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6072 | 15844 | if (*op->d.rpr_nav.offset_isnull) | 24cfb8dRow pattern recognition patch (executor and commands).
6073 | 28 | ereport(ERROR, | 24cfb8dRow pattern recognition patch (executor and commands).
6074 | - | errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | 24cfb8dRow pattern recognition patch (executor and commands).
6075 | - | errmsg("row pattern navigation offset must not be null")); | 24cfb8dRow pattern recognition patch (executor and commands).
6076 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6077 | 15816 | offset = DatumGetInt64(*op->d.rpr_nav.offset_value); | 24cfb8dRow pattern recognition patch (executor and commands).
6078 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6079 | 15816 | if (offset < 0) | 24cfb8dRow pattern recognition patch (executor and commands).
6080 | 24 | ereport(ERROR, | 24cfb8dRow pattern recognition patch (executor and commands).
6081 | - | errcode(ERRCODE_INVALID_PARAMETER_VALUE), | 24cfb8dRow pattern recognition patch (executor and commands).
6082 | - | errmsg("row pattern navigation offset must not be negative")); | 24cfb8dRow pattern recognition patch (executor and commands).
6083 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6084 | - | else | 24cfb8dRow pattern recognition patch (executor and commands).
6085 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6086 | - | /* Default offset: 1 for simple PREV/NEXT, 0 otherwise */ | 24cfb8dRow pattern recognition patch (executor and commands).
6087 | 968180 | if (op->d.rpr_nav.kind == RPR_NAV_PREV || | 24cfb8dRow pattern recognition patch (executor and commands).
6088 | - | op->d.rpr_nav.kind == RPR_NAV_NEXT) | 24cfb8dRow pattern recognition patch (executor and commands).
6089 | - | offset = 1; | 24cfb8dRow pattern recognition patch (executor and commands).
6090 | - | else | 24cfb8dRow pattern recognition patch (executor and commands).
6091 | 944 | offset = 0; | 24cfb8dRow pattern recognition patch (executor and commands).
6092 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6093 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6094 | - | /* | 24cfb8dRow pattern recognition patch (executor and commands).
6095 | - | * Calculate target position based on navigation direction. On overflow, | 24cfb8dRow pattern recognition patch (executor and commands).
6096 | - | * use -1 so that ExecRPRNavGetSlot treats it as out of range. | 24cfb8dRow pattern recognition patch (executor and commands).
6097 | - | */ | 24cfb8dRow pattern recognition patch (executor and commands).
6098 | 983972 | switch (op->d.rpr_nav.kind) | 24cfb8dRow pattern recognition patch (executor and commands).
6099 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6100 | 972208 | case RPR_NAV_PREV: | 24cfb8dRow pattern recognition patch (executor and commands).
6101 | 972208 | if (pg_sub_s64_overflow(winstate->currentpos, offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6102 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| UnreachableDefensive (unreachable) · confidence high · `ExecEvalRPRNavSet @6102` Reason
    Confirmed defensive-unreachable.
    Line 6102 (target_pos = -1 on subtraction underflow for RPR_NAV_PREV) cannot be reached by any SQL input.
    The subtraction is pg_sub_s64_overflow(winstate->currentpos, offset, &target_pos). currentpos is declared int64 in execnodes.h:2612 and is a window-partition row index, always in [0, nrows-1], hence >= 0. offset is read via DatumGetInt64 (line 6077) and validated to be non-negative at lines 6079-6082 (negative -> ereport ERROR), or set to a small default (0/1) when the offset arg is absent.
    For currentpos >= 0 and offset >= 0, the result currentpos - offset ranges from currentpos (<= INT64_MAX, no overflow) down to 0 - INT64_MAX = -INT64_MAX = INT64_MIN + 1, which is strictly greater than INT64_MIN and therefore representable.
    Underflow is mathematically impossible, so pg_sub_s64_overflow always returns false and the guarded assignment on 6102 is never executed.
    I tried to refute this: the only way to make currentpos - offset underflow would require offset > currentpos - INT64_MIN, i.e. offset > INT64_MAX (since the most negative reachable currentpos is 0), which int64 offset cannot hold;
    and a negative currentpos is structurally impossible.
    No SQL input reaches the line.
Recommended fix
    Keep the guard but document it as defensive, or convert to an assertion since underflow is impossible.
    Option A (preferred for safety/symmetry with the other arms): leave the code and add a comment such as "currentpos >= 0 and offset >= 0, so subtraction cannot underflow;
    guard kept for symmetry with NEXT/FIRST/LAST".
    Option B: compute target_pos = winstate->currentpos - offset;
    and Assert(!pg_sub_s64_overflow(winstate->currentpos, offset, &target_pos));
    to express the invariant and drop the unreachable runtime branch.
    Note this asymmetry: the NEXT arm (6105, pg_add_s64_overflow) and FIRST arm (6110) CAN overflow for large offsets and are genuinely reachable, so only the PREV (6101-6102) overflow branch is provably dead.
6103 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6104 | 572 | case RPR_NAV_NEXT: | 24cfb8dRow pattern recognition patch (executor and commands).
6105 | 572 | if (pg_add_s64_overflow(winstate->currentpos, offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6106 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| ReachableTestable · confidence high · `ExecEvalRPRNavSet @6106` How to test
    Add to rpr.sql a
    DEFINE using a max-int8 NEXT offset on a >=2-row, all-true-predicate match so the matcher evaluates at currentpos>=1 (forcing overflow at line 6106):

    SELECT val, count(*) OVER w AS grp
    FROM (VALUES (1),(2),(3)) AS t(val)
    WINDOW w AS (
    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
    PATTERN (A+)
    DEFINE A AS NEXT(val, 9223372036854775807) IS NULL
    )
    ORDER BY val;

    Expected output (verified on a live build):
     val | grp
    -----+-----
       1 |   3
       2 |   0
       3 |   0

    The A+ match starts at row 0 and extends through currentpos 1 and 2;
    at currentpos>=1, currentpos + 9223372036854775807 overflows -> line 6106 (target_pos = -1) -> NEXT returns NULL -> IS NULL TRUE -> all 3 rows in one match group (grp=3 on the first row under FINAL count).
    Note: the predicate is also TRUE at currentpos=0 (no overflow there, INT64_MAX is simply out of range), but extension to currentpos>=1 is what executes 6106. To also cover the PREV overflow sibling (line 6102), add a symmetric
    DEFINE A AS PREV(val, 9223372036854775807) IS NULL on the same data (currentpos - INT64_MAX underflows).
Verdict
    Confirmed testable and currently uncovered.
    Line 6106 (target_pos = -1 on RPR_NAV_NEXT overflow) fires when currentpos + offset > INT64_MAX.
    The offset is coerced to int8 (parse_func.c:2204) and only validated >= 0 (execExprInterp.c:6079), so INT64_MAX is a legal offset with no upper bound.
    The existing suite's largest NEXT offset is NEXT(val,999) over 1000 rows (rpr.sql:812), giving max ~1998 -- never overflows, so 6106 is genuinely uncovered.
    I empirically proved on a live RPR build that
    DEFINE predicates are evaluated at currentpos >= 1 (an A+ match over val < NEXT(val,1) extended through currentpos 1 and 2), so currentpos + INT64_MAX overflows there and executes 6106;
    target_pos becomes -1, ExecRPRNavGetSlot returns nav_null_slot, NEXT(...) yields NULL.
    The proposed test ran and produced correct classification output.
    Caveat: per-line gcov confirmation was not obtainable because the installed binary is an LTO coverage build that zeroed/mis-attributed line counts (whole function showed ##### despite executing);
    reliance is on the behavioral discriminator test, which is conclusive.
6107 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6108 | 628 | case RPR_NAV_FIRST: | 24cfb8dRow pattern recognition patch (executor and commands).
6109 | - | /* FIRST: offset from match_start, clamped to currentpos */ | 24cfb8dRow pattern recognition patch (executor and commands).
6110 | 628 | if (pg_add_s64_overflow(winstate->nav_match_start, offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6111 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6112 | 628 | else if (target_pos > winstate->currentpos) | 24cfb8dRow pattern recognition patch (executor and commands).
6113 | 68 | target_pos = -1; /* beyond current match range */ | 24cfb8dRow pattern recognition patch (executor and commands).
6114 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6115 | 580 | case RPR_NAV_LAST: | 24cfb8dRow pattern recognition patch (executor and commands).
6116 | - | /* LAST: offset backward from currentpos, clamped to match_start */ | 24cfb8dRow pattern recognition patch (executor and commands).
6117 | 580 | if (pg_sub_s64_overflow(winstate->currentpos, offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6118 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6119 | 580 | else if (target_pos < winstate->nav_match_start) | 24cfb8dRow pattern recognition patch (executor and commands).
6120 | 68 | target_pos = -1; /* before match_start */ | 24cfb8dRow pattern recognition patch (executor and commands).
6121 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6122 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6123 | 9804 | case RPR_NAV_PREV_FIRST: | 24cfb8dRow pattern recognition patch (executor and commands).
6124 | - | case RPR_NAV_NEXT_FIRST: | 24cfb8dRow pattern recognition patch (executor and commands).
6125 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6126 | 9804 | int64 compound_offset; | 24cfb8dRow pattern recognition patch (executor and commands).
6127 | 9804 | int64 inner_pos; | 24cfb8dRow pattern recognition patch (executor and commands).
6128 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6129 | - | /* Inner: match_start + offset */ | 24cfb8dRow pattern recognition patch (executor and commands).
6130 | 9804 | if (pg_add_s64_overflow(winstate->nav_match_start, offset, &inner_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6131 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6132 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6133 | 983964 | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6134 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6135 | 9804 | if (inner_pos > winstate->currentpos || inner_pos < 0) | 24cfb8dRow pattern recognition patch (executor and commands).
6136 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6137 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6138 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6139 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6140 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6141 | - | /* Outer offset */ | 24cfb8dRow pattern recognition patch (executor and commands).
6142 | 9672 | compound_offset = rpr_nav_get_compound_offset(op); | 24cfb8dRow pattern recognition patch (executor and commands).
6143 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6144 | - | /* Apply outer: PREV subtracts, NEXT adds */ | 24cfb8dRow pattern recognition patch (executor and commands).
6145 | 9668 | if (op->d.rpr_nav.kind == RPR_NAV_PREV_FIRST) | 24cfb8dRow pattern recognition patch (executor and commands).
6146 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6147 | 9564 | if (pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6148 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| UnreachableDefensive (unreachable) · confidence high · `ExecEvalRPRNavSet @6148` Reason
    Confirmed unreachable.
    Line 6148 (target_pos = -1) fires only if pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos) reports underflow in the RPR_NAV_PREV_FIRST branch.
    Both operands are provably non-negative: inner_pos is constrained to [0, currentpos] by the guard at lines 6135-6139 (inner_pos > currentpos || inner_pos < 0 => break), and compound_offset is validated >= 0 by rpr_nav_get_compound_offset (line 6034 rejects val < 0;
    NULL rejected at 6027).
    Subtracting a non-negative from a non-negative cannot underflow int64: the most negative achievable result is 0 - INT64_MAX = -(2^63 - 1) = INT64_MIN + 1, which is still representable (INT64_MIN = -(2^63)).
    Underflow would require compound_offset > INT64_MAX, impossible for an int64. I attempted refutation with the largest possible outer literal (PREV(FIRST(...), 9223372036854775807) over an empty/zero-position match giving inner_pos=0): result is -INT64_MAX, no overflow, so 6148 is NOT taken (the value is simply treated as out-of-range downstream by ExecRPRNavGetSlot).
    The compound PREV(FIRST(...), N) syntax is real and exercised in rpr.sql (lines 560-577), so the branch is reachable structurally but the overflow=true outcome is mathematically impossible.
    Note the sibling pg_add branches (NEXT_FIRST line 6152, NEXT_LAST line 6187) CAN overflow and are reachable;
    only the two pg_sub overflow arms (6148 and 6182) are dead because both their operands are non-negative.
Recommended fix
    Replace the unreachable overflow handling with an Assert documenting the invariant, e.g.:
      /* inner_pos in [0,currentpos] and compound_offset >= 0, so no underflow */
      Assert(!pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos));
    target_pos = inner_pos - compound_offset;
    The same simplification applies to the RPR_NAV_PREV_LAST sub-overflow arm at line 6182 (inner_pos there is also non-negative: line 6170 enforces inner_pos >= nav_match_start >= 0).
    Keep the pg_add_s64_overflow handling for the NEXT_FIRST/NEXT_LAST arms unchanged -- those are genuinely reachable.
6149 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6150 | - | else | 24cfb8dRow pattern recognition patch (executor and commands).
6151 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6152 | 104 | if (pg_add_s64_overflow(inner_pos, compound_offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6153 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| ReachableTestable · confidence high · `ExecEvalRPRNavSet @6153` How to test
    Either form covers line 6153 on a partition with >=2 matched rows.
    Most robust (overflows on every match independent of match_start) -- add to src/test/regress/sql/rpr.sql near the other compound-overflow tests:

    -- Compound NEXT(FIRST): outer offset = INT64_MAX overflows int64 -> target -1 -> NULL
    SELECT id, val, count(*) OVER w AS cnt
    FROM rpr_nav
    WINDOW w AS (
    ORDER BY id
    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
    PATTERN (A B+)
    DEFINE A AS TRUE, B AS NEXT(FIRST(val, 1), 9223372036854775807) IS NULL
    );

    The inner offset 1 forces inner_pos = match_start + 1 >= 1, so inner_pos + 9223372036854775807 always overflows regardless of where the match starts.
    The proposed test (NEXT(FIRST(val), 9223372036854775807) IS NULL, inner default 0) also works because later match attempts have match_start >= 1, but it relies on match-restart positioning;
    the inner-offset-1 form is preferred for determinism.
    Verified via gcov: line 6153 hit count 4, line 6152 overflow branch taken 100%.
Verdict
    CONFIRMED reachable and the proposed test covers line 6153. I built and ran the RPR-instrumented binary (PostgreSQL 19beta1 from tmp_install) against a fresh cluster, executed the proposed test, and gcov reported line 6153 executed (count 4) with line 6152's overflow branch taken.
    Line 6153 is the overflow handler in the RPR_NAV_NEXT_FIRST case: pg_add_s64_overflow(inner_pos, compound_offset, &target_pos) -> target_pos=-1. compound_offset is the user-supplied outer offset of NEXT(FIRST(expr), N), validated only >=0 in rpr_nav_get_compound_offset (execExprInterp.c:6034), so it can be INT64_MAX. inner_pos is partition-relative [match_start..currentpos];
    it is >=1 whenever a match starts at partition position >=1 OR an inner offset >=1 is used, so inner_pos + INT64_MAX overflows.
    Skeptical note: the proposed test with inner default offset 0 does NOT overflow on the FIRST match (match_start=0 -> inner_pos=0 -> 0+INT64_MAX=INT64_MAX, no overflow, hits success line 6152), but it DOES overflow on subsequent match attempts whose match_start>=1;
    with a >=5-row partition this happens, hence the proposed test still reaches 6153. The output (IS NULL = TRUE) is the same for overflow and out-of-range, so the test's pass/fail cannot by itself prove which line ran, but gcov proves coverage.
    Existing rpr.sql:1185 (NEXT(FIRST(val),99)) exercises this case via the success path only (99 never overflows).
6154 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6155 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6156 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6157 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6158 | 180 | case RPR_NAV_PREV_LAST: | 24cfb8dRow pattern recognition patch (executor and commands).
6159 | - | case RPR_NAV_NEXT_LAST: | 24cfb8dRow pattern recognition patch (executor and commands).
6160 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6161 | 180 | int64 compound_offset; | 24cfb8dRow pattern recognition patch (executor and commands).
6162 | 180 | int64 inner_pos; | 24cfb8dRow pattern recognition patch (executor and commands).
6163 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6164 | - | /* Inner: currentpos - offset */ | 24cfb8dRow pattern recognition patch (executor and commands).
6165 | 180 | if (pg_sub_s64_overflow(winstate->currentpos, offset, &inner_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6166 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6167 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6168 | 983964 | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6169 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6170 | 180 | if (inner_pos < winstate->nav_match_start) | 24cfb8dRow pattern recognition patch (executor and commands).
6171 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6172 | - | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
6173 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6174 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6175 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6176 | - | /* Outer offset */ | 24cfb8dRow pattern recognition patch (executor and commands).
6177 | 72 | compound_offset = rpr_nav_get_compound_offset(op); | 24cfb8dRow pattern recognition patch (executor and commands).
6178 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6179 | - | /* Apply outer: PREV subtracts, NEXT adds */ | 24cfb8dRow pattern recognition patch (executor and commands).
6180 | 68 | if (op->d.rpr_nav.kind == RPR_NAV_PREV_LAST) | 24cfb8dRow pattern recognition patch (executor and commands).
6181 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6182 | 24 | if (pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6183 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| UnreachableDefensive (unreachable) · confidence high · `ExecEvalRPRNavSet @6183` Reason
    CONFIRMED defensive-unreachable.
    Line 6183 is the target_pos=-1 assignment inside the `if (pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos))` branch for RPR_NAV_PREV_LAST.
    To reach it, inner_pos - compound_offset must underflow below INT64_MIN.
    But the surrounding code guarantees:
    (1) inner_pos = currentpos - offset where offset>=0 (validated 6079-6082), so inner_pos <= currentpos;
    (2) line 6170-6174 forces inner_pos >= nav_match_start, and nav_match_start is a real row position set from matchStartRow or 0 (nodeWindowAgg.c:2831, 4462), hence >= 0;
    so inner_pos is in [0, currentpos], a small non-negative bounded by partition size;
    (3) compound_offset is validated >= 0 (rpr_nav_get_compound_offset, lines 6034-6037).
    The minimum possible value of inner_pos - compound_offset is 0 - INT64_MAX = -(2^63-1) = INT64_MIN+1, which is within int64 range.
    Therefore pg_sub_s64_overflow can never return true here.
    Negative or NULL offsets are rejected with ereport(ERROR) before this point, so no SQL/regression input can construct an underflowing subtraction.
    I tried to refute by maximizing compound_offset (bigint up to INT64_MAX) against inner_pos=0, but the result is still exactly representable.
    Genuinely unreachable.
Recommended fix
    Replace the unreachable overflow check with an Assert documenting the invariant, e.g.:

    if (op->d.rpr_nav.kind == RPR_NAV_PREV_LAST)
    {
        /*
         * inner_pos is in [nav_match_start, currentpos] (>= 0) and
         * compound_offset >= 0, so the subtraction cannot underflow.
         */
        Assert(!pg_sub_s64_overflow(inner_pos, compound_offset, &target_pos));
    target_pos = inner_pos - compound_offset;
    }

    Alternatively, keep the defensive overflow branch but add a comment noting it is unreachable given inner_pos>=0 and compound_offset>=0, to document why it is intentionally untested.
6184 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6185 | - | else | 24cfb8dRow pattern recognition patch (executor and commands).
6186 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6187 | 44 | if (pg_add_s64_overflow(inner_pos, compound_offset, &target_pos)) | 24cfb8dRow pattern recognition patch (executor and commands).
6188 | 0 | target_pos = -1; | 24cfb8dRow pattern recognition patch (executor and commands).
| ReachableTestable · confidence high · `ExecEvalRPRNavSet @6188` How to test
    Add to src/test/regress/sql/rpr.sql (after the existing compound tests near line 1204), using the existing rpr_nav table:

    -- Compound: outer offset overflow on NEXT(LAST()) -> target_pos = -1 via add overflow (covers execExprInterp.c:6188)
    SELECT id, val, count(*) OVER w AS cnt
    FROM rpr_nav
    WINDOW w AS (
    ORDER BY id
    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
    PATTERN (A+)
    DEFINE A AS NEXT(LAST(val), 9223372036854775807) IS NULL
    );

    Expected output: all 6 rows match (A+ forms a single match from row 1);
    the row at id=1 shows cnt=6 and rows id=2..6 show cnt=0, because NEXT(LAST(val), INT64_MAX) overflows to NULL at every row, making the
    DEFINE predicate uniformly TRUE.
    (Verified empirically.) For symmetry, the analogous PREV(LAST()) underflow path uses pg_sub_s64_overflow at line 6182-6183;
    if that sibling line is also uncovered, add:
    DEFINE A AS PREV(LAST(val), 9223372036854775807) IS NULL similarly (inner_pos = currentpos small, currentpos - INT64_MAX underflows -> -1).
Verdict
    CONFIRMED testable, and the proposed test hits the exact line.
    Line 6188 sets target_pos = -1 when pg_add_s64_overflow overflows in the RPR_NAV_NEXT_LAST branch (inner_pos + compound_offset).
    Reaching it requires:
    (1) inner offset of LAST valid >=0;
    (2) inner_pos = currentpos - inner_offset not overflowing (line 6165) and inner_pos >= nav_match_start (line 6170);
    (3) outer offset of NEXT validated >=0 (rpr_nav_get_compound_offset);
    (4) inner_pos + outer_offset overflows int64. The proposed test NEXT(LAST(val), 9223372036854775807) achieves all four:
    LAST inner offset defaults to 0, so inner_pos = currentpos (small, non-negative, always >= nav_match_start, passes lines 6165/6170), then currentpos + INT64_MAX overflows for any currentpos >= 1, hitting line 6187-6188. I empirically verified against a live RPR build (PG19beta1, fresh temp cluster).
    The proposed query returned TRUE (IS NULL) for the entire matched partition starting at row 1. A control with outer offset 1 returned TRUE only at the final row (legitimate out-of-range via nav_null_slot, NOT the overflow branch) and FALSE elsewhere.
    This divergence proves the INT64_MAX form forces the overflow branch at 6188 rather than the ordinary out-of-range path.
    Existing tests (e.g.
    NEXT(LAST(val,99),1) at rpr.sql:1203) trip the inner clamp at line 6170 instead and never reach 6188. Note: the proposed root cause text slightly mislabels the default inner offset as currentpos-0 being driven by user offset;
    the LAST inner offset is the default 0, which is exactly why inner_pos stays small and valid, so the explanation holds in effect.
6189 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6190 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6191 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6192 | 0 | default: | 24cfb8dRow pattern recognition patch (executor and commands).
| UnreachableDefensive (unreachable) · confidence high · `ExecEvalRPRNavSet @6192-6193` · 2 lines Reason
    Confirmed defensive-unreachable.
    The switch at execExprInterp.c:6098 dispatches on op->d.rpr_nav.kind, an RPRNavKind enum (primnodes.h:669-680) with EXACTLY 8 members:
    RPR_NAV_PREV/NEXT/FIRST/LAST and the four compounds PREV_FIRST/PREV_LAST/NEXT_FIRST/NEXT_LAST.
    Every one of the 8 has an explicit case label (lines 6100,6104,6108,6115,6123,6124,6158,6159), so the default at 6192-6193 matches no valid enum value.
    The kind field is populated by the expression compiler (EEOP_RPR_NAV_SET) from a parser/planner-validated RPRNav node, never from user-controlled raw integers.
    I tried to refute by looking for any path that sets kind from a cast/user value or an enum value lacking a case;
    there is none.
    The default elog(ERROR) is reachable only via memory corruption or an invalid cast, neither of which any SQL or regression input can produce.
    This is the canonical PostgreSQL can't-happen guard for an exhaustive enum switch.
Recommended fix
    No source change required.
    Keep the default elog(ERROR) as a standard defensive guard for an exhaustive switch over an enum, consistent with PostgreSQL convention.
    It is intentionally non-testable.
6193 | 0 | elog(ERROR, "unrecognized RPR navigation kind: %d", | 24cfb8dRow pattern recognition patch (executor and commands).
6194 | - | op->d.rpr_nav.kind); | 24cfb8dRow pattern recognition patch (executor and commands).
6195 | - | break; | 24cfb8dRow pattern recognition patch (executor and commands).
6196 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6197 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6198 | - | /* | 24cfb8dRow pattern recognition patch (executor and commands).
6199 | - | * Slot swap elision: if target_pos is the current row, skip the | 24cfb8dRow pattern recognition patch (executor and commands).
6200 | - | * tuplestore fetch and slot swap entirely. This benefits LAST(expr), | 24cfb8dRow pattern recognition patch (executor and commands).
6201 | - | * PREV(expr, 0), NEXT(expr, 0), and similar cases. | 24cfb8dRow pattern recognition patch (executor and commands).
6202 | - | * | 24cfb8dRow pattern recognition patch (executor and commands).
6203 | - | * We must still set nav_saved_outertuple (done above) so that | 24cfb8dRow pattern recognition patch (executor and commands).
6204 | - | * EEOP_RPR_NAV_RESTORE is a harmless no-op. | 24cfb8dRow pattern recognition patch (executor and commands).
6205 | - | */ | 24cfb8dRow pattern recognition patch (executor and commands).
6206 | 983964 | if (target_pos == winstate->currentpos) | 24cfb8dRow pattern recognition patch (executor and commands).
6207 | 983964 | return; | 24cfb8dRow pattern recognition patch (executor and commands).
6208 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6209 | - | /* Fetch target row slot (returns nav_null_slot if out of range) */ | 24cfb8dRow pattern recognition patch (executor and commands).
6210 | 983092 | target_slot = ExecRPRNavGetSlot(winstate, target_pos); | 24cfb8dRow pattern recognition patch (executor and commands).
6211 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6212 | - | /* | 24cfb8dRow pattern recognition patch (executor and commands).
6213 | - | * Update econtext to point to the target slot. Also decompress the new | 24cfb8dRow pattern recognition patch (executor and commands).
6214 | - | * slot's attributes since FETCHSOME already ran for the original slot. | 24cfb8dRow pattern recognition patch (executor and commands).
6215 | - | * The caller (interpreter or JIT) is responsible for updating any local | 24cfb8dRow pattern recognition patch (executor and commands).
6216 | - | * slot cache (e.g. outerslot) from econtext after we return. | 24cfb8dRow pattern recognition patch (executor and commands).
6217 | - | */ | 24cfb8dRow pattern recognition patch (executor and commands).
6218 | 983092 | slot_getallattrs(target_slot); | 24cfb8dRow pattern recognition patch (executor and commands).
6219 | 983092 | econtext->ecxt_outertuple = target_slot; | 24cfb8dRow pattern recognition patch (executor and commands).
6220 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).

ExecEvalRPRNavRestore() lines 6238-6257
Modified Lines Coverage: 11/11 lines (100.0%)

Line | Hits | Source | Commit
--- | --- | --- | ---
6238 | 983964 | ExecEvalRPRNavRestore(ExprState *state, ExprEvalStep *op, | 24cfb8dRow pattern recognition patch (executor and commands).
6239 | - | ExprContext *econtext) | 24cfb8dRow pattern recognition patch (executor and commands).
6240 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6241 | 983964 | WindowAggState *winstate = op->d.rpr_nav.winstate; | 24cfb8dRow pattern recognition patch (executor and commands).
6242 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6243 | 983964 | econtext->ecxt_outertuple = winstate->nav_saved_outertuple; | 24cfb8dRow pattern recognition patch (executor and commands).
6244 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6245 | - | /* Stabilize pass-by-ref result against nav_slot re-fetch */ | 24cfb8dRow pattern recognition patch (executor and commands).
6246 | 983964 | if (!op->d.rpr_nav.resulttypbyval && | 24cfb8dRow pattern recognition patch (executor and commands).
6247 | 144604 | !*op->resnull) | 24cfb8dRow pattern recognition patch (executor and commands).
6248 | - | { | 24cfb8dRow pattern recognition patch (executor and commands).
6249 | 130112 | MemoryContext oldContext; | 24cfb8dRow pattern recognition patch (executor and commands).
6250 | - |  | 24cfb8dRow pattern recognition patch (executor and commands).
6251 | 130112 | oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); | 24cfb8dRow pattern recognition patch (executor and commands).
6252 | 260224 | *op->resvalue = datumCopy(*op->resvalue, | 24cfb8dRow pattern recognition patch (executor and commands).
6253 | - | false, | 24cfb8dRow pattern recognition patch (executor and commands).
6254 | 130112 | op->d.rpr_nav.resulttyplen); | 24cfb8dRow pattern recognition patch (executor and commands).
6255 | 130112 | MemoryContextSwitchTo(oldContext); | 24cfb8dRow pattern recognition patch (executor and commands).
6256 | - | } | 24cfb8dRow pattern recognition patch (executor and commands).
6257 | 983964 | } | 24cfb8dRow pattern recognition patch (executor and commands).