src/backend/nodes/outfuncs.c | 14 +++++++ src/backend/optimizer/path/allpaths.c | 3 ++ src/backend/optimizer/plan/createplan.c | 32 ++++++++++++++++ src/backend/optimizer/util/pathnode.c | 68 +++++++++++++++++++++++++++++++++ src/include/nodes/nodes.h | 2 + src/include/nodes/relation.h | 30 +++++++++++++++ src/include/optimizer/pathnode.h | 26 +++++++++++++ 7 files changed, 175 insertions(+) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e686a6c..29a12bd 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"); @@ -3059,6 +3070,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..d5b0fb3 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -77,6 +77,7 @@ 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 Plan *create_custom_plan(PlannerInfo *root, CustomPath *best_path); 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, @@ -261,6 +262,9 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) plan = create_unique_plan(root, (UniquePath *) best_path); break; + case T_CustomScan: + plan = create_custom_plan(root, (CustomPath *) best_path); + break; default: elog(ERROR, "unrecognized node type: %d", (int) best_path->pathtype); @@ -1072,6 +1076,34 @@ 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 'scan_clauses' and targetlist 'tlist'. + */ +static Plan * +create_custom_plan(PlannerInfo *root, CustomPath *best_path) +{ + Plan *custom_plan; + + /* + * Create a CustomScan (or its inheritance) according to + * the supplied CustomPath. + */ + Assert(best_path->path.pathtype == T_CustomScan); + custom_plan = (Plan *) + best_path->methods->CreateCustomPlan(root, best_path); + Assert(nodeTag(custom_plan) == best_path->path.pathtype); + + /* + * Copy cost data from Path to Plan; no need to make custom-plan + * providers do this + */ + copy_path_costsize(custom_plan, &best_path->path); + + return custom_plan; +} /***************************************************************************** * diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 319e8b2..b723bdd 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,70 @@ reparameterize_path(PlannerInfo *root, Path *path, } return NULL; } + +/***************************************************************************** + * creation of custom-plan paths + *****************************************************************************/ +typedef struct +{ + const char *cpp_name; + uint32 cpp_class; + add_custom_path_type cpp_callback; +} custom_path_provider_info; + +static List *custom_path_providers = NIL; + +/* + * register_custom_path_provider + * + * It registers an entrypoint of custom-path providers. + * The callback function is expected to construct CustomPath node to run + * alternative logic instead of the existing node, if extension can do. + */ +void +register_custom_path_provider(const char *cpp_name, + int cpp_class, + add_custom_path_type callback) +{ + custom_path_provider_info *cpp_info; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + cpp_info = palloc(sizeof(custom_path_provider_info)); + cpp_info->cpp_name = pstrdup(cpp_name); + cpp_info->cpp_class = cpp_class; + cpp_info->cpp_callback = callback; + + custom_path_providers = lappend(custom_path_providers, cpp_info); + + 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) +{ + customScanArg sarg; + ListCell *cell; + + sarg.cpp_class = CUSTOM_PATH_CLASS_SCAN; + sarg.root = root; + sarg.baserel = baserel; + sarg.rte = rte; + + foreach (cell, custom_path_providers) + { + custom_path_provider_info *cpp_info = lfirst(cell); + + if (cpp_info->cpp_class == CUSTOM_PATH_CLASS_SCAN) + cpp_info->cpp_callback(&sarg); + } +} diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index a031b88..6376784 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/relation.h b/src/include/nodes/relation.h index dacbe9c..188cc54 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" @@ -881,6 +882,35 @@ 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; + +#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; + Node *(*CreateCustomPlan)(PlannerInfo *root, + CustomPath *best_path); + 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..b6c6284 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -129,6 +129,32 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path, double loop_count); /* + * Interface definition of custom-scan providers + */ +#define CUSTOM_PATH_CLASS_SCAN 's' +/* XXX - Other custom class paths shall be added in the later version */ + +/* + * customScanArg - A set of arguments for relation scan. + */ +typedef struct { + int cpp_class; /* = CUSTOM_PATH_CLASS_SCAN */ + PlannerInfo *root; + RelOptInfo *baserel; + RangeTblEntry *rte; +} customScanArg; + +typedef void (*add_custom_path_type)(void *arg); + +extern void register_custom_path_provider(const char *cpp_name, + int cpp_class, + add_custom_path_type callback); + +extern void create_customscan_paths(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + +/* * prototypes for relnode.c */ extern void setup_simple_rel_arrays(PlannerInfo *root);