src/backend/nodes/outfuncs.c | 14 +++++++ src/backend/optimizer/path/allpaths.c | 3 ++ src/backend/optimizer/plan/createplan.c | 73 +++++++++++++++++++++++++++++++++ src/backend/optimizer/util/pathnode.c | 50 ++++++++++++++++++++++ src/include/nodes/nodes.h | 2 + src/include/nodes/plannodes.h | 9 ++++ src/include/nodes/relation.h | 37 +++++++++++++++++ src/include/optimizer/pathnode.h | 9 ++++ 8 files changed, 197 insertions(+) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 1ff78eb..8ad9c8d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1582,6 +1582,17 @@ _outForeignPath(StringInfo str, const ForeignPath *node) } static void +_outCustomPath(StringInfo str, const CustomPath *node) +{ + WRITE_NODE_TYPE("CUSTOMPATH"); + _outPathInfo(str, (const Path *) node); + WRITE_UINT_FIELD(flags); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomPath(str, node); +} + +static void _outAppendPath(StringInfo str, const AppendPath *node) { WRITE_NODE_TYPE("APPENDPATH"); @@ -3060,6 +3071,9 @@ _outNode(StringInfo str, const void *obj) case T_ForeignPath: _outForeignPath(str, obj); break; + case T_CustomPath: + _outCustomPath(str, obj); + break; case T_AppendPath: _outAppendPath(str, obj); break; diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index c81efe9..8b42e36 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -402,6 +402,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Consider TID scans */ create_tidscan_paths(root, rel); + /* Consider custom scans, if any */ + create_customscan_paths(root, rel, rte); + /* Now find the cheapest of the paths for this rel */ set_cheapest(rel); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4b641a2..189f7ef 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -77,6 +77,9 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa List *tlist, List *scan_clauses); static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses); +static CustomScan *create_customscan_plan(PlannerInfo *root, + CustomPath *best_path, + List *tlist, List *scan_clauses); static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path, Plan *outer_plan, Plan *inner_plan); static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path, @@ -233,6 +236,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) case T_CteScan: case T_WorkTableScan: case T_ForeignScan: + case T_CustomScan: plan = create_scan_plan(root, best_path); break; case T_HashJoin: @@ -409,6 +413,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path) scan_clauses); break; + case T_CustomScan: + plan = (Plan *) create_customscan_plan(root, + (CustomPath *) best_path, + tlist, + scan_clauses); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) best_path->pathtype); @@ -1072,6 +1083,68 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) return plan; } +/* + * create_custom_plan + * + * Returns a custom-scan plan for the base relation scanned by 'best_path' + * with restriction clauses 'clauses' and targetlist 'tlist'. + */ +static CustomScan * +create_customscan_plan(PlannerInfo *root, CustomPath *best_path, + List *tlist, List *scan_clauses) +{ + CustomScan *custom_scan; + RelOptInfo *rel = best_path->path.parent; + + /* + * Right now, all we can support is CustomScan node which is associated + * with a particular base relation to be scanned. + */ + Assert(rel && rel->reloptkind == RELOPT_BASEREL); + + /* + * Sort clauses into the best execution order, although custom-scan + * provider can reorder them again. + */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + /* + * Create a CustomScan (or its inheritance) node according to + * the supplied CustomPath. + */ + custom_scan = (CustomScan *) + best_path->methods->PlanCustomPath(root, + rel, + best_path, + tlist, + scan_clauses); + /* additional sanity checks */ + Assert(nodeTag(custom_scan) == best_path->path.pathtype); + if (IsA(custom_scan, CustomScan)) + { + Index scanrelid = ((Scan *)custom_scan)->scanrelid; + + if (scanrelid != rel->relid) + elog(ERROR, "Bug? CustomScan tries to scan incorrect relation"); + } + else + elog(ERROR, "unexpected node: %d", (int)nodeTag(custom_scan)); + + /* + * NOTE: unlike create_foreignscan_plan(), it is responsibility of + * the custom plan provider to replace outer-relation variables + * with nestloop params, because we cannot know how many expression + * trees are held in the private fields. + */ + + /* + * Copy cost data from Path to Plan; no need to make custom-plan + * providers do this + */ + copy_path_costsize((Plan *)custom_scan, &best_path->path); + + return custom_scan; +} /***************************************************************************** * diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 319e8b2..2ca0a18 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -27,6 +27,7 @@ #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/selfuncs.h" @@ -1926,3 +1927,52 @@ reparameterize_path(PlannerInfo *root, Path *path, } return NULL; } + +/***************************************************************************** + * creation of custom-plan paths + *****************************************************************************/ + +static List *custom_path_providers = NIL; + +/* + * register_custom_path_provider + * + * It registers a table of callback functions that implements a custom-path + * provider. The callback functions are expected to construct CustomPath node + * that provides an alternative logic to scan a relation (and so on in the + * future version), if extension can do. + * Note that the supplied CustomPathMethods is expected to locate on static + * memory area, so we don't copy individual fields here. + */ +void +register_custom_path_provider(CustomPathMethods *cpp_methods) +{ + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + custom_path_providers = lappend(custom_path_providers, cpp_methods); + MemoryContextSwitchTo(oldcxt); +} + +/* + * create_customscan_paths + * + * It calls back extension's entrypoint whether it can add alternative + * scan paths being extended from the CustomPath type. + * If any, the callback will add one or more paths using add_path(). + */ +void +create_customscan_paths(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte) +{ + ListCell *cell; + + foreach (cell, custom_path_providers) + { + const CustomPathMethods *cpp_methods = lfirst(cell); + + if (cpp_methods->CreateCustomScanPath) + cpp_methods->CreateCustomScanPath(root, baserel, rte); + } +} diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 154d943..6584a2c 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -62,6 +62,7 @@ typedef enum NodeTag T_CteScan, T_WorkTableScan, T_ForeignScan, + T_CustomScan, T_Join, T_NestLoop, T_MergeJoin, @@ -224,6 +225,7 @@ typedef enum NodeTag T_HashPath, T_TidPath, T_ForeignPath, + T_CustomPath, T_AppendPath, T_MergeAppendPath, T_ResultPath, diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 1839494..e08fb1d 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -482,6 +482,15 @@ typedef struct ForeignScan bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; +/* ---------------- + * CustomScan node + * ---------------- + */ +typedef struct CustomScan +{ + Scan scan; + uint32 flags; /* mask of CUSTOMPATH_* flags defined in relation.h */ +} CustomScan; /* * ========== diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index f1a0504..2bbb7cc 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -15,6 +15,7 @@ #define RELATION_H #include "access/sdir.h" +#include "lib/stringinfo.h" #include "nodes/params.h" #include "nodes/parsenodes.h" #include "storage/block.h" @@ -884,6 +885,42 @@ typedef struct ForeignPath } ForeignPath; /* + * CustomPath represents a scan using custom logic + * + * Extension (that performs as custom-plan provider) can adds an alternative + * path using its custom type being delivered from CustomPath. + * They can store their private data on the extra fields of their custom + * object. A set of common methods are represented as function pointers in + * CustomPathMethods structure; extension has to set up then correctly. + */ +struct CustomPathMethods; +struct Plan; /* not to include plannodes.h here */ + +#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN 0x0001 +#define CUSTOMPATH_SUPPORT_MARK_RESTORE 0x0002 + +typedef struct CustomPath +{ + Path path; + uint32 flags; + const struct CustomPathMethods *methods; +} CustomPath; + +typedef struct CustomPathMethods +{ + const char *CustomName; + void (*CreateCustomScanPath)(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + struct Plan *(*PlanCustomPath)(PlannerInfo *root, + RelOptInfo *rel, + CustomPath *best_path, + List *tlist, + List *clauses); + void (*TextOutCustomPath)(StringInfo str, const CustomPath *node); +} CustomPathMethods; + +/* * AppendPath represents an Append plan, ie, successive execution of * several member plans. * diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index a0bcc82..8d75020 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -129,6 +129,15 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path, double loop_count); /* + * Interface definition of custom-scan providers + */ +extern void register_custom_path_provider(CustomPathMethods *cpp_methods); + +extern void create_customscan_paths(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + +/* * prototypes for relnode.c */ extern void setup_simple_rel_arrays(PlannerInfo *root);