1414
1515#include "access/amapi.h"
1616#include "access/htup_details.h"
17+ #include "access/tableam.h"
1718#include "executor/execdebug.h"
19+ #include "catalog/pg_class.h"
1820#include "executor/nodeAgg.h"
1921#include "executor/nodeAppend.h"
2022#include "executor/nodeBitmapAnd.h"
6163#include "nodes/extensible.h"
6264#include "nodes/nodeFuncs.h"
6365#include "nodes/pathnodes.h"
66+ #include "parser/parsetree.h"
6467#include "utils/rel.h"
6568#include "utils/syscache.h"
6669
6770static bool IndexSupportsBackwardScan (Oid indexid );
71+ static bool TableSupportsBackwardScan (Oid tableid );
6872
6973
7074/*
@@ -509,7 +513,7 @@ ExecSupportsMarkRestore(Path *pathnode)
509513 * children do. Therefore, this routine must be passed a complete plan tree.
510514 */
511515bool
512- ExecSupportsBackwardScan (Plan * node )
516+ ExecSupportsBackwardScan (Plan * node , List * rtable )
513517{
514518 if (node == NULL )
515519 return false;
@@ -526,7 +530,7 @@ ExecSupportsBackwardScan(Plan *node)
526530 {
527531 case T_Result :
528532 if (outerPlan (node ) != NULL )
529- return ExecSupportsBackwardScan (outerPlan (node ));
533+ return ExecSupportsBackwardScan (outerPlan (node ), rtable );
530534 else
531535 return false;
532536
@@ -540,7 +544,7 @@ ExecSupportsBackwardScan(Plan *node)
540544
541545 foreach (l , ((Append * ) node )-> appendplans )
542546 {
543- if (!ExecSupportsBackwardScan ((Plan * ) lfirst (l )))
547+ if (!ExecSupportsBackwardScan ((Plan * ) lfirst (l ), rtable ))
544548 return false;
545549 }
546550 /* need not check tlist because Append doesn't evaluate it */
@@ -561,7 +565,7 @@ ExecSupportsBackwardScan(Plan *node)
561565 return IndexSupportsBackwardScan (((IndexOnlyScan * ) node )-> indexid );
562566
563567 case T_SubqueryScan :
564- return ExecSupportsBackwardScan (((SubqueryScan * ) node )-> subplan );
568+ return ExecSupportsBackwardScan (((SubqueryScan * ) node )-> subplan , rtable );
565569
566570 case T_CustomScan :
567571 if (((CustomScan * ) node )-> flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN )
@@ -571,6 +575,16 @@ ExecSupportsBackwardScan(Plan *node)
571575 case T_SeqScan :
572576 case T_TidScan :
573577 case T_TidRangeScan :
578+ {
579+ RangeTblEntry * rte ;
580+
581+ Assert (((Scan * ) node )-> scanrelid > 0 &&
582+ ((Scan * ) node )-> scanrelid <= list_length (rtable ));
583+
584+ rte = rt_fetch (((Scan * ) node )-> scanrelid , rtable );
585+ return TableSupportsBackwardScan (rte -> relid );
586+ }
587+
574588 case T_FunctionScan :
575589 case T_ValuesScan :
576590 case T_CteScan :
@@ -589,7 +603,7 @@ ExecSupportsBackwardScan(Plan *node)
589603
590604 case T_LockRows :
591605 case T_Limit :
592- return ExecSupportsBackwardScan (outerPlan (node ));
606+ return ExecSupportsBackwardScan (outerPlan (node ), rtable );
593607
594608 default :
595609 return false;
@@ -625,6 +639,34 @@ IndexSupportsBackwardScan(Oid indexid)
625639 return result ;
626640}
627641
642+ /*
643+ * An SeqScan, TidScan or TidRangeScan node supports backward scan only if the
644+ * table's AM does.
645+ */
646+ static bool
647+ TableSupportsBackwardScan (Oid tableid )
648+ {
649+ bool result ;
650+ HeapTuple ht_tabrel ;
651+ Form_pg_class tabrelrec ;
652+ const TableAmRoutine * amroutine ;
653+
654+ /* Fetch the pg_class tuple of the index relation */
655+ ht_tabrel = SearchSysCache1 (RELOID , ObjectIdGetDatum (tableid ));
656+ if (!HeapTupleIsValid (ht_tabrel ))
657+ elog (ERROR , "cache lookup failed for relation %u" , tableid );
658+ tabrelrec = (Form_pg_class ) GETSTRUCT (ht_tabrel );
659+
660+ /* Fetch the table AM's API struct */
661+ amroutine = GetTableAmRoutineByAmOid (tabrelrec -> relam );
662+
663+ result = amroutine -> amcanbackward ;
664+
665+ ReleaseSysCache (ht_tabrel );
666+
667+ return result ;
668+ }
669+
628670/*
629671 * ExecMaterializesOutput - does a plan type materialize its output?
630672 *
0 commit comments