doc/src/sgml/custom-scan.sgml | 31 +++++++++++++++++++++++++++++++
doc/src/sgml/fdwhandler.sgml | 22 ++++++++++++++++++++++
src/backend/executor/nodeLimit.c | 20 +++++++++++++++++---
src/include/executor/nodeLimit.h | 2 ++
src/include/foreign/fdwapi.h | 5 +++++
src/include/nodes/extensible.h | 4 ++++
6 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 1ca9247..515110a 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -350,6 +350,37 @@ void (*ExplainCustomScan) (CustomScanState *node,
be shown even without this callback, but the callback allows the display
of additional, private state.
+
+
+
+void (*PassDownLimitBound) (CustomScanState *node,
+ LimitState *lstate);
+
+ Allows special optimization during execution time
+ if and when CustomScanState> is located under
+ under LimitState>; which implies the underlying node is not
+ required to generate all the possible rows but up to a particular number
+ of rows according to the LIMIT> clause.
+ This callback is optional.
+
+
+ LIMIT> clause usually takes a constant value but not all.
+ On the planning time, PlannerInfo> may inform us
+ an optimization hint if it is a constant value, however, we cannot always
+ know the exact number of rows that LimitState> must produce.
+ This callback tells the custom-scan provider the exact number of rows
+ on execution time even if LIMIT> clause takes an expression,
+ then, the custom-scan provider can adjust its behavior according to
+ the information.
+
+
+ It may be an idea to have an alternative execution path if your extension
+ is fully optimized to the case when LIMIT> requires a small
+ number of rows, but uncertain at planning time.
+ It is a situation we cannot know the best path until execution time.
+ We don't support reconstruction of a plan tree once constructed, but we
+ can switch internal behavior of CustomScan>.
+
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 0c1db07..afb2057 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1256,6 +1256,28 @@ InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc,
+
+ FDW Routines for Miscellaneous Purpose
+
+ A ForeignScan> node also has miscellaneous callbacks for
+ corner case optimization and so on.
+
+
+
+
+void
+PassDownLimitBound(ForeignScanState *node, LimitState *lstate);
+
+ Allows special optimization if ForeignScanState> is located
+ under LimitState> thus we can preliminary know exact number of
+ rows to be produced on the query execution time.
+ This callback is optional.
+
+
+ See the relevant API at for more
+ detailed dicsussion.
+
+
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index faf32e1..00a66b9 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -23,11 +23,11 @@
#include "executor/executor.h"
#include "executor/nodeLimit.h"
+#include "foreign/fdwapi.h"
+#include "nodes/extensible.h"
#include "nodes/nodeFuncs.h"
static void recompute_limits(LimitState *node);
-static void pass_down_bound(LimitState *node, PlanState *child_node);
-
/* ----------------------------------------------------------------
* ExecLimit
@@ -315,7 +315,7 @@ recompute_limits(LimitState *node)
* changes of these parameters. If we ever do redesign this, it'd be a
* good idea to integrate this signaling with the parameter-change mechanism.
*/
-static void
+void
pass_down_bound(LimitState *node, PlanState *child_node)
{
if (IsA(child_node, SortState))
@@ -360,6 +360,20 @@ pass_down_bound(LimitState *node, PlanState *child_node)
!expression_returns_set((Node *) child_node->plan->targetlist))
pass_down_bound(node, outerPlanState(child_node));
}
+ else if (IsA(child_node, ForeignScanState))
+ {
+ ForeignScanState *fss = (ForeignScanState *) child_node;
+
+ if (fss->fdwroutine->PassDownLimitBound)
+ fss->fdwroutine->PassDownLimitBound(fss, node);
+ }
+ else if (IsA(child_node, CustomScanState))
+ {
+ CustomScanState *css = (CustomScanState *) child_node;
+
+ if (css->methods->PassDownLimitBound)
+ css->methods->PassDownLimitBound(css, node);
+ }
}
/* ----------------------------------------------------------------
diff --git a/src/include/executor/nodeLimit.h b/src/include/executor/nodeLimit.h
index 96166b4..04ee9a2 100644
--- a/src/include/executor/nodeLimit.h
+++ b/src/include/executor/nodeLimit.h
@@ -21,4 +21,6 @@ extern TupleTableSlot *ExecLimit(LimitState *node);
extern void ExecEndLimit(LimitState *node);
extern void ExecReScanLimit(LimitState *node);
+extern void pass_down_bound(LimitState *node, PlanState *child_node);
+
#endif /* NODELIMIT_H */
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index e1b0d0d..19851db 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -154,6 +154,8 @@ typedef void (*InitializeWorkerForeignScan_function) (ForeignScanState *node,
typedef bool (*IsForeignScanParallelSafe_function) (PlannerInfo *root,
RelOptInfo *rel,
RangeTblEntry *rte);
+typedef void (*PassDownLimitBound_function) (ForeignScanState *node,
+ LimitState *lstate);
/*
* FdwRoutine is the struct returned by a foreign-data wrapper's handler
@@ -224,6 +226,9 @@ typedef struct FdwRoutine
EstimateDSMForeignScan_function EstimateDSMForeignScan;
InitializeDSMForeignScan_function InitializeDSMForeignScan;
InitializeWorkerForeignScan_function InitializeWorkerForeignScan;
+
+ /* Special optimization function if ForeignScan under LIMIT */
+ PassDownLimitBound_function PassDownLimitBound;
} FdwRoutine;
diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h
index 17afe58..ccdce29 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -144,6 +144,10 @@ typedef struct CustomExecMethods
void (*ExplainCustomScan) (CustomScanState *node,
List *ancestors,
ExplainState *es);
+
+ /* Optional: special optimization if CustomScan under LIMIT node */
+ void (*PassDownLimitBound) (CustomScanState *node,
+ LimitState *lstate);
} CustomExecMethods;
extern void RegisterCustomScanMethods(const CustomScanMethods *methods);