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);