diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 7d6828db403..354f1f225c3 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -2964,6 +2964,7 @@ heapam_reloptions(char relkind, Datum reloptions, bool validate) static const TableAmRoutine heapam_methods = { .type = T_TableAmRoutine, + .amcanbackward = true, .slot_callbacks = heapam_slot_callbacks, .get_row_ref_type = heapam_get_row_ref_type, diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 4f6acf67198..afee706025a 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -134,7 +134,7 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { if (plan->rowMarks == NIL && - ExecSupportsBackwardScan(plan->planTree)) + ExecSupportsBackwardScan(plan->planTree, plan->rtable)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 1a7f6ae2c9b..430bf53c127 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -14,6 +14,8 @@ #include "access/amapi.h" #include "access/htup_details.h" +#include "access/tableam.h" +#include "catalog/pg_am_d.h" #include "catalog/pg_class.h" #include "executor/nodeAgg.h" #include "executor/nodeAppend.h" @@ -60,9 +62,11 @@ #include "executor/nodeWorktablescan.h" #include "nodes/extensible.h" #include "nodes/pathnodes.h" +#include "parser/parsetree.h" #include "utils/syscache.h" static bool IndexSupportsBackwardScan(Oid indexid); +static bool TableSupportsBackwardScan(Oid tableid); /* @@ -507,7 +511,7 @@ ExecSupportsMarkRestore(Path *pathnode) * children do. Therefore, this routine must be passed a complete plan tree. */ bool -ExecSupportsBackwardScan(Plan *node) +ExecSupportsBackwardScan(Plan *node, List *rtable) { if (node == NULL) return false; @@ -524,7 +528,7 @@ ExecSupportsBackwardScan(Plan *node) { case T_Result: if (outerPlan(node) != NULL) - return ExecSupportsBackwardScan(outerPlan(node)); + return ExecSupportsBackwardScan(outerPlan(node), rtable); else return false; @@ -538,7 +542,7 @@ ExecSupportsBackwardScan(Plan *node) foreach(l, ((Append *) node)->appendplans) { - if (!ExecSupportsBackwardScan((Plan *) lfirst(l))) + if (!ExecSupportsBackwardScan((Plan *) lfirst(l), rtable)) return false; } /* need not check tlist because Append doesn't evaluate it */ @@ -559,7 +563,7 @@ ExecSupportsBackwardScan(Plan *node) return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid); case T_SubqueryScan: - return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan); + return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan, rtable); case T_CustomScan: if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) @@ -569,6 +573,16 @@ ExecSupportsBackwardScan(Plan *node) case T_SeqScan: case T_TidScan: case T_TidRangeScan: + { + RangeTblEntry *rte; + + Assert(((Scan *) node)->scanrelid > 0 && + ((Scan *) node)->scanrelid <= list_length(rtable)); + + rte = rt_fetch(((Scan *) node)->scanrelid, rtable); + return TableSupportsBackwardScan(rte->relid); + } + case T_FunctionScan: case T_ValuesScan: case T_CteScan: @@ -587,7 +601,7 @@ ExecSupportsBackwardScan(Plan *node) case T_LockRows: case T_Limit: - return ExecSupportsBackwardScan(outerPlan(node)); + return ExecSupportsBackwardScan(outerPlan(node), rtable); default: return false; @@ -623,6 +637,34 @@ IndexSupportsBackwardScan(Oid indexid) return result; } +/* + * An SeqScan, TidScan or TidRangeScan node supports backward scan only if the + * table's AM does. + */ +static bool +TableSupportsBackwardScan(Oid tableid) +{ + bool result; + HeapTuple ht_tabrel; + Form_pg_class tabrelrec; + const TableAmRoutine *amroutine; + + /* Fetch the pg_class tuple of the index relation */ + ht_tabrel = SearchSysCache1(RELOID, ObjectIdGetDatum(tableid)); + if (!HeapTupleIsValid(ht_tabrel)) + elog(ERROR, "cache lookup failed for relation %u", tableid); + tabrelrec = (Form_pg_class) GETSTRUCT(ht_tabrel); + + /* Fetch the table AM's API struct */ + amroutine = GetTableAmRoutineByAmOid(tabrelrec->relam); + + result = amroutine->amcanbackward; + + ReleaseSysCache(ht_tabrel); + + return result; +} + /* * ExecMaterializesOutput - does a plan type materialize its output? * diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index cf07d47e958..ae4a8949313 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1697,7 +1697,8 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, if (list_length(stmt_list) == 1 && linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY && linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL && - ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree)) + ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree, + linitial_node(PlannedStmt, stmt_list)->rtable)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index eefae492133..15e9895fba9 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -426,7 +426,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, */ if (cursorOptions & CURSOR_OPT_SCROLL) { - if (!ExecSupportsBackwardScan(top_plan)) + if (!ExecSupportsBackwardScan(top_plan, root->parse->rtable)) top_plan = materialize_finished_plan(top_plan); } diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index 5c55a5f78a5..09175483dce 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -312,6 +312,8 @@ typedef struct TableAmRoutine /* this must be set to T_TableAmRoutine */ NodeTag type; + /* does AM support backward scanning? */ + bool amcanbackward; /* ------------------------------------------------------------------------ * Slot related callbacks. diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 1833f4d84b1..cc32f43fd69 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -106,7 +106,7 @@ extern void ExecReScan(PlanState *node); extern void ExecMarkPos(PlanState *node); extern void ExecRestrPos(PlanState *node); extern bool ExecSupportsMarkRestore(struct Path *pathnode); -extern bool ExecSupportsBackwardScan(Plan *node); +extern bool ExecSupportsBackwardScan(Plan *node, List *rtable); extern bool ExecMaterializesOutput(NodeTag plantype); /*