src/backend/commands/explain.c | 84 ++++++++++++++++++ src/backend/executor/Makefile | 2 +- src/backend/executor/execAmi.c | 39 +++++++++ src/backend/nodes/copyfuncs.c | 77 ++++++++++++++++ src/backend/nodes/outfuncs.c | 49 +++++++++++ src/backend/optimizer/path/allpaths.c | 23 +++++ src/backend/optimizer/path/joinpath.c | 19 ++++ src/backend/optimizer/plan/createplan.c | 150 +++++++++++++++++++++++++++++++- src/backend/optimizer/util/pathnode.c | 110 +++++++++++++++++++++++ src/include/nodes/execnodes.h | 45 ++++++++++ src/include/nodes/nodes.h | 9 ++ src/include/nodes/plannodes.h | 38 ++++++++ src/include/nodes/relation.h | 24 +++++ src/include/optimizer/pathnode.h | 26 ++++++ src/include/optimizer/paths.h | 25 ++++++ 15 files changed, 718 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 91bea51..e4d0120 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -19,6 +19,7 @@ #include "commands/defrem.h" #include "commands/prepare.h" #include "executor/hashjoin.h" +#include "executor/nodeCustom.h" #include "foreign/fdwapi.h" #include "optimizer/clauses.h" #include "parser/parsetree.h" @@ -84,6 +85,7 @@ static void show_hash_info(HashState *hashstate, ExplainState *es); static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es); static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es); +static void show_custom_info(Node *cstate, ExplainState *es); static const char *explain_get_index_name(Oid indexId); static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir, ExplainState *es); @@ -861,6 +863,9 @@ ExplainNode(PlanState *planstate, List *ancestors, pname = "Hash"; /* "Join" gets added by jointype switch */ sname = "Hash Join"; break; + case T_CustomJoin: + pname = sname = "Custom Join"; + break; case T_SeqScan: pname = sname = "Seq Scan"; break; @@ -897,6 +902,9 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_ForeignScan: pname = sname = "Foreign Scan"; break; + case T_CustomScan: + pname = sname = "Custom Scan"; + break; case T_Material: pname = sname = "Materialize"; break; @@ -958,6 +966,9 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_Limit: pname = sname = "Limit"; break; + case T_CustomPlan: + pname = sname = "Custom Plan"; + break; case T_Hash: pname = sname = "Hash"; break; @@ -1011,6 +1022,7 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_CteScan: case T_WorkTableScan: case T_ForeignScan: + case T_CustomScan: ExplainScanTarget((Scan *) plan, es); break; case T_IndexScan: @@ -1051,6 +1063,7 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_NestLoop: case T_MergeJoin: case T_HashJoin: + case T_CustomJoin: { const char *jointype; @@ -1291,6 +1304,13 @@ ExplainNode(PlanState *planstate, List *ancestors, planstate, es); show_foreignscan_info((ForeignScanState *) planstate, es); break; + case T_CustomScan: + show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 1, + planstate, es); + show_custom_info((Node *) planstate, es); + break; case T_NestLoop: show_upper_qual(((NestLoop *) plan)->join.joinqual, "Join Filter", planstate, ancestors, es); @@ -1328,6 +1348,14 @@ ExplainNode(PlanState *planstate, List *ancestors, show_instrumentation_count("Rows Removed by Filter", 2, planstate, es); break; + case T_CustomJoin: + show_upper_qual(((HashJoin *) plan)->join.joinqual, + "Join Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 2, + planstate, es); + show_custom_info((Node *) planstate, es); + break; case T_Agg: case T_Group: show_upper_qual(plan->qual, "Filter", planstate, ancestors, es); @@ -1357,6 +1385,13 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_Hash: show_hash_info((HashState *) planstate, es); break; + case T_CustomPlan: + show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 1, + planstate, es); + show_custom_info((Node *) planstate, es); + break; default: break; } @@ -1477,6 +1512,7 @@ ExplainNode(PlanState *planstate, List *ancestors, IsA(plan, BitmapAnd) || IsA(plan, BitmapOr) || IsA(plan, SubqueryScan) || + (IsA(plan, CustomPlan) && ((CustomPlan *) plan)->custom_subplans) || planstate->subPlan; if (haschildren) { @@ -1531,6 +1567,12 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors, "Subquery", NULL, es); break; + case T_CustomPlan: + if (((CustomPlan *) plan)->custom_subplans) + ExplainMemberNodes(((CustomPlan *) plan)->custom_subplans, + ((CustomPlanState *) planstate)->custom_subplans, + ancestors, es); + break; default: break; } @@ -1858,6 +1900,35 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es) } /* + * Show extra information for Custom(Plan|Scan|Join) node + */ +static void +show_custom_info(Node *cstate, ExplainState *es) +{ + CustomProvider *provider; + const char *custom_name; + + switch (nodeTag(cstate)) + { + case T_CustomPlan: + custom_name = ((CustomPlan *) cstate)->custom_name; + break; + case T_CustomScan: + custom_name = ((CustomScan *) cstate)->custom_name; + break; + case T_CustomJoin: + custom_name = ((CustomJoin *) cstate)->custom_name; + break; + default: + return; + } + + provider = get_custom_provider(custom_name); + if (provider->ExplainCustom) + (*provider->ExplainCustom)(cstate, es); +} + +/* * Fetch the name of an index in an EXPLAIN * * We allow plugins to get control here so that plans involving hypothetical @@ -2025,6 +2096,19 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es) objectname = rte->ctename; objecttag = "CTE Name"; break; + case T_CustomScan: + if (rte->rtekind == RTE_RELATION) + { + objectname = get_rel_name(rte->relid); + if (es->verbose) + namespace = get_namespace_name(get_rel_namespace(rte->relid)); + objecttag = "Relation Name"; + } + else + { + /* TODO: add support for other rtekind */ + } + break; default: break; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 6081b56..af707b0 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -16,7 +16,7 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \ execProcnode.o execQual.o execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ - nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ + nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeCustom.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \ nodeLimit.o nodeLockRows.o \ nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \ diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index a078104..9cc6bdd 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -21,6 +21,7 @@ #include "executor/nodeBitmapIndexscan.h" #include "executor/nodeBitmapOr.h" #include "executor/nodeCtescan.h" +#include "executor/nodeCustom.h" #include "executor/nodeForeignscan.h" #include "executor/nodeFunctionscan.h" #include "executor/nodeGroup.h" @@ -149,6 +150,10 @@ ExecReScan(PlanState *node) ExecReScanBitmapOr((BitmapOrState *) node); break; + case T_CustomPlanState: + ExecReScanCustomPlan((CustomPlanState *) node); + break; + case T_SeqScanState: ExecReScanSeqScan((SeqScanState *) node); break; @@ -197,6 +202,10 @@ ExecReScan(PlanState *node) ExecReScanForeignScan((ForeignScanState *) node); break; + case T_CustomScanState: + ExecReScanCustomScan((CustomScanState *) node); + break; + case T_NestLoopState: ExecReScanNestLoop((NestLoopState *) node); break; @@ -209,6 +218,10 @@ ExecReScan(PlanState *node) ExecReScanHashJoin((HashJoinState *) node); break; + case T_CustomJoinState: + ExecReScanCustomJoin((CustomJoinState *) node); + break; + case T_MaterialState: ExecReScanMaterial((MaterialState *) node); break; @@ -421,6 +434,9 @@ ExecSupportsMarkRestore(NodeTag plantype) bool ExecSupportsBackwardScan(Plan *node) { + ListCell *cell; + uint flags; + if (node == NULL) return false; @@ -461,6 +477,29 @@ ExecSupportsBackwardScan(Plan *node) return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid) && TargetListSupportsBackwardScan(node->targetlist); + case T_CustomPlan: + flags = ((CustomPlan *) node)->custom_flags; + if ((flags & CUSTOM_FLAGS_SUPPORT_BACKWARD_SCAN) == 0) + return false; + + foreach(cell, ((CustomPlan *) node)->custom_subplans) + { + if (!ExecSupportsBackwardScan((Plan *) lfirst(cell))) + return false; + } + if (outerPlan(node) && !ExecSupportsBackwardScan(outerPlan(node))) + return false; + if (innerPlan(node) && !ExecSupportsBackwardScan(innerPlan(node))) + return false; + return TargetListSupportsBackwardScan(node->targetlist); + + case T_CustomScan: + flags = ((CustomScan *) node)->custom_flags; + if ((flags & CUSTOM_FLAGS_SUPPORT_BACKWARD_SCAN) == 0) + return false; + + return TargetListSupportsBackwardScan(node->targetlist); + case T_SubqueryScan: return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) && TargetListSupportsBackwardScan(node->targetlist); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 65f3b98..974cd67 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -304,6 +304,27 @@ _copyBitmapOr(const BitmapOr *from) return newnode; } +/* + * _copyCustomPlan + */ +static CustomPlan * +_copyCustomPlan(const CustomPlan *from) +{ + CustomPlan *newnode = makeNode(CustomPlan); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_STRING_FIELD(custom_name); + COPY_SCALAR_FIELD(custom_flags); + COPY_NODE_FIELD(custom_private); + COPY_NODE_FIELD(custom_expr); + COPY_NODE_FIELD(custom_subplans); + + return newnode; +} /* * CopyScanFields @@ -602,6 +623,30 @@ _copyForeignScan(const ForeignScan *from) } /* + * _copyCustomScan + */ +static CustomScan * +_copyCustomScan(const CustomScan *from) +{ + CustomScan *newnode = makeNode(CustomScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_STRING_FIELD(custom_name); + COPY_SCALAR_FIELD(custom_flags); + COPY_NODE_FIELD(custom_private); + COPY_NODE_FIELD(custom_expr); + + return newnode; +} + +/* * CopyJoinFields * * This function copies the fields of the Join node. It is used by @@ -707,6 +752,29 @@ _copyHashJoin(const HashJoin *from) return newnode; } +/* + * _copyCustomJoin + */ +static CustomJoin * +_copyCustomJoin(const CustomJoin *from) +{ + CustomJoin *newnode = makeNode(CustomJoin); + + /* + * copy node superclass fields + */ + CopyJoinFields((const Join *) from, (Join *) newnode); + + /* + * copy remainder of node + */ + COPY_STRING_FIELD(custom_name); + COPY_SCALAR_FIELD(custom_flags); + COPY_NODE_FIELD(custom_private); + COPY_NODE_FIELD(custom_expr); + + return newnode; +} /* * _copyMaterial @@ -3890,6 +3958,9 @@ copyObject(const void *from) case T_BitmapOr: retval = _copyBitmapOr(from); break; + case T_CustomPlan: + retval = _copyCustomPlan(from); + break; case T_Scan: retval = _copyScan(from); break; @@ -3929,6 +4000,9 @@ copyObject(const void *from) case T_ForeignScan: retval = _copyForeignScan(from); break; + case T_CustomScan: + retval = _copyCustomScan(from); + break; case T_Join: retval = _copyJoin(from); break; @@ -3941,6 +4015,9 @@ copyObject(const void *from) case T_HashJoin: retval = _copyHashJoin(from); break; + case T_CustomJoin: + retval = _copyCustomJoin(from); + break; case T_Material: retval = _copyMaterial(from); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index a2903f9..d260763 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -423,6 +423,20 @@ _outBitmapOr(StringInfo str, const BitmapOr *node) } static void +_outCustomPlan(StringInfo str, const CustomPlan *node) +{ + WRITE_NODE_TYPE("CUSTOMPLAN"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_STRING_FIELD(custom_name); + WRITE_UINT_FIELD(custom_flags); + WRITE_NODE_FIELD(custom_private); + WRITE_NODE_FIELD(custom_exprs); + WRITE_NODE_FIELD(custom_subplans); +} + +static void _outScan(StringInfo str, const Scan *node) { WRITE_NODE_TYPE("SCAN"); @@ -568,6 +582,19 @@ _outForeignScan(StringInfo str, const ForeignScan *node) } static void +_outCustomScan(StringInfo str, const CustomScan *node) +{ + WRITE_NODE_TYPE("CUSTOMSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_STRING_FIELD(custom_name); + WRITE_UINT_FIELD(custom_flags); + WRITE_NODE_FIELD(custom_private); + WRITE_NODE_FIELD(custom_exprs); +} + +static void _outJoin(StringInfo str, const Join *node) { WRITE_NODE_TYPE("JOIN"); @@ -627,6 +654,19 @@ _outHashJoin(StringInfo str, const HashJoin *node) } static void +_outCustomJoin(StringInfo str, const CustomJoin *node) +{ + WRITE_NODE_TYPE("CUSTOMJOIN"); + + _outJoinPlanInfo(str, (const Join *) node); + + WRITE_STRING_FIELD(custom_name); + WRITE_UINT_FIELD(custom_flags); + WRITE_NODE_FIELD(custom_private); + WRITE_NODE_FIELD(custom_exprs); +} + +static void _outAgg(StringInfo str, const Agg *node) { int i; @@ -2775,6 +2815,9 @@ _outNode(StringInfo str, const void *obj) case T_BitmapOr: _outBitmapOr(str, obj); break; + case T_CustomPlan: + _outCustomPlan(str, obj); + break; case T_Scan: _outScan(str, obj); break; @@ -2814,6 +2857,9 @@ _outNode(StringInfo str, const void *obj) case T_ForeignScan: _outForeignScan(str, obj); break; + case T_CustomScan: + _outCustomScan(str, obj); + break; case T_Join: _outJoin(str, obj); break; @@ -2826,6 +2872,9 @@ _outNode(StringInfo str, const void *obj) case T_HashJoin: _outHashJoin(str, obj); break; + case T_CustomJoin: + _outCustomJoin(str, obj); + break; case T_Agg: _outAgg(str, obj); break; diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index bfd3809..9d0cbf5 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -46,6 +46,8 @@ int geqo_threshold; /* Hook for plugins to replace standard_join_search() */ join_search_hook_type join_search_hook = NULL; +/* Hook for plugins to add custom scan paths */ +add_scan_path_hook_type add_scan_path_hook = NULL; static void set_base_rel_sizes(PlannerInfo *root); static void set_base_rel_pathlists(PlannerInfo *root); @@ -399,6 +401,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Consider TID scans */ create_tidscan_paths(root, rel); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Now find the cheapest of the paths for this rel */ set_cheapest(rel); } @@ -427,6 +432,9 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Call the FDW's GetForeignPaths function to generate path(s) */ rel->fdwroutine->GetForeignPaths(root, rel, rte->relid); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path */ set_cheapest(rel); } @@ -1246,6 +1254,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Generate appropriate path */ add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer)); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); } @@ -1269,6 +1280,9 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_functionscan_path(root, rel, required_outer)); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); } @@ -1292,6 +1306,9 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_valuesscan_path(root, rel, required_outer)); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); } @@ -1361,6 +1378,9 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_ctescan_path(root, rel, required_outer)); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); } @@ -1414,6 +1434,9 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_worktablescan_path(root, rel, required_outer)); + /* Consider Custom scans */ + add_custom_scan_paths(root,rel,rte); + /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); } diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 5b477e5..162b21d 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -21,6 +21,9 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" +/* Hook for plugins to add custom join paths */ +add_join_path_hook_type add_join_path_hook = NULL; + #define PATH_PARAM_BY_REL(path, rel) \ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids)) @@ -259,6 +262,22 @@ add_paths_to_joinrel(PlannerInfo *root, restrictlist, jointype, sjinfo, &semifactors, param_source_rels, extra_lateral_rels); + + /* + * 5. Also consider paths being provided with custom execution provider. + */ + if (add_join_path_hook) + (*add_join_path_hook)(root, + joinrel, + outerrel, + innerrel, + jointype, + sjinfo, + restrictlist, + mergeclause_list, + &semifactors, + param_source_rels, + extra_lateral_rels); } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 9b9eb2f..a165a9b 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -21,6 +21,7 @@ #include "access/skey.h" #include "catalog/pg_class.h" +#include "executor/nodeCustom.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -77,12 +78,20 @@ 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, + CustomScanPath *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, Plan *outer_plan, Plan *inner_plan); static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); +static CustomJoin *create_customjoin_plan(PlannerInfo *root, + CustomJoinPath *best_path, + Plan *outer_plan, + Plan *inner_plan); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); static void process_subquery_nestloop_params(PlannerInfo *root, @@ -235,11 +244,13 @@ 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: case T_MergeJoin: case T_NestLoop: + case T_CustomJoin: plan = create_join_plan(root, (JoinPath *) best_path); break; @@ -411,6 +422,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path) scan_clauses); break; + case T_CustomScan: + plan = (Plan *)create_customscan_plan(root, + (CustomScanPath *)best_path, + tlist, + scan_clauses); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) best_path->pathtype); @@ -607,9 +625,14 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) outer_plan = create_plan_recurse(root, best_path->outerjoinpath); /* For a nestloop, include outer relids in curOuterRels for inner side */ - if (best_path->path.pathtype == T_NestLoop) + if (best_path->path.pathtype == T_NestLoop || + (best_path->path.pathtype == T_CustomJoin && + (((CustomJoinPath *) best_path)->custom_flags & + CUSTOM_FLAGS_APPLY_PARAMS_ON_INNER) != 0)) + { root->curOuterRels = bms_union(root->curOuterRels, best_path->outerjoinpath->parent->relids); + } inner_plan = create_plan_recurse(root, best_path->innerjoinpath); @@ -637,6 +660,17 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) outer_plan, inner_plan); break; + case T_CustomJoin: + if (saveOuterRels != root->curOuterRels) + { + bms_free(root->curOuterRels); + root->curOuterRels = saveOuterRels; + } + plan = (Plan *)create_customjoin_plan(root, + (CustomJoinPath *) best_path, + outer_plan, + inner_plan); + break; default: elog(ERROR, "unrecognized node type: %d", (int) best_path->path.pathtype); @@ -2016,6 +2050,51 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, return scan_plan; } +/* + * create_customscan_plan + * Returns a custom-scan plan for the base relation scanned by 'best_path' + * with restriction clauses 'scan_clauses' and targetlist 'tlist'. + */ +static CustomScan * +create_customscan_plan(PlannerInfo *root, + CustomScanPath *best_path, + List *tlist, + List *scan_clauses) +{ + CustomProvider *provider = get_custom_provider(best_path->custom_name); + CustomScan *scan_plan; + RelOptInfo *rel = best_path->path.parent; + Index scan_relid = rel->relid; + RangeTblEntry *rte; + + /* it should be a base rel */ + Assert(scan_relid > 0); + rte = planner_rt_fetch(scan_relid, root); + + /* Sort clauses into best execution order. */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + scan_plan = provider->GetCustomScan(root, rel, rte, + best_path, + tlist, + scan_clauses); + Assert(IsA(scan_plan, CustomScan)); + Assert(strcmp(scan_plan->custom_name, provider->name) == 0); + + /* Copy cost data from Path to Plan; no need to make callback do this */ + copy_path_costsize(&scan_plan->scan.plan, &best_path->path); + + /* Replace any outer-relation variables with nestloop params in the qual */ + if (best_path->path.param_info) + { + scan_plan->scan.plan.qual = (List *) + replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual); + scan_plan->custom_exprs = (List *) + replace_nestloop_params(root, (Node *) scan_plan->custom_exprs); + } + + return scan_plan; +} /***************************************************************************** * @@ -2534,6 +2613,69 @@ create_hashjoin_plan(PlannerInfo *root, return join_plan; } +CustomJoin * +create_customjoin_plan(PlannerInfo *root, + CustomJoinPath *best_path, + Plan *outer_plan, + Plan *inner_plan) +{ + CustomProvider *provider = get_custom_provider(best_path->custom_name); + RelOptInfo *joinrel = best_path->jpath.path.parent; + CustomJoin *join_plan; + List *join_clauses; + List *other_clauses; + List *tlist; + + /* + * Construct target-list of this join path because build_path_tlist() + * is static function. + */ + tlist = build_path_tlist(root, &best_path->jpath.path); + + /* Sort join qual clauses into best execution order */ + join_clauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); + + + /* Get the join qual clauses (in plain expression form) */ + /* Any pseudoconstant clauses are ignored here */ + if (IS_OUTER_JOIN(best_path->jpath.jointype)) + { + extract_actual_join_clauses(join_clauses, + &join_clauses, &other_clauses); + } + else + { + /* We can treat all clauses alike for an inner join */ + join_clauses = extract_actual_clauses(join_clauses, false); + other_clauses = NIL; + } + + /* Construct CustomJoin node */ + join_plan = provider->GetCustomJoin(root, + joinrel, + best_path, + tlist, + join_clauses, + other_clauses, + outer_plan, + inner_plan); + Assert(IsA(join_plan, CustomJoin)); + Assert(strcmp(join_plan->custom_name, provider->name) == 0); + + /* Copy cost data from Path to Plan; no need to make callback do this */ + copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); + + /* Replace any outer-relation variables with nestloop params */ + if (best_path->jpath.path.param_info) + { + join_clauses = (List *) + replace_nestloop_params(root, (Node *) join_clauses); + other_clauses = (List *) + replace_nestloop_params(root, (Node *) other_clauses); + } + + return join_plan; +} /***************************************************************************** * @@ -4863,6 +5005,12 @@ is_projection_capable_plan(Plan *plan) case T_MergeAppend: case T_RecursiveUnion: return false; + /* + * XXX - Due to nature of CustomScan and CustomJoin nodes, they heve + * to be capable for projection. It depends on implementation if + * CustomPlan, however, core PostgreSQL does not add CustomPlan node + * right now. + */ default: break; } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 64b17051..7dc51be 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1738,6 +1738,46 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, } /* + * create_customscan_path + * Creates a path corresponding to a scan of a relation based on logic + * logic being provided by extensions. + * + * This function is never called from core PostgreSQL. An usual usage is + * invocation from callbacks on add_scan_path_hook. We don't have any + * assumption on the custom scan logic, thus, caller is responsible to + * set adequate cost estimation here. + */ +CustomScanPath * +create_customscan_path(PlannerInfo *root, + RelOptInfo *baserel, + double rows, + Cost startup_cost, + Cost total_cost, + List *pathkeys, + Relids required_outer, + const char *custom_name, + uint32 custom_flags, + List *custom_private) +{ + CustomScanPath *pathnode = makeNode(CustomScanPath); + + pathnode->path.pathtype = T_CustomScan; + pathnode->path.parent = baserel; + pathnode->path.param_info = get_baserel_parampathinfo(root, baserel, + required_outer); + pathnode->path.rows = rows; + pathnode->path.startup_cost = startup_cost; + pathnode->path.total_cost = total_cost; + pathnode->path.pathkeys = pathkeys; + + pathnode->custom_name = pstrdup(custom_name); + pathnode->custom_flags = custom_flags; + pathnode->custom_private = custom_private; + + return pathnode; +} + +/* * calc_nestloop_required_outer * Compute the required_outer set for a nestloop join path * @@ -2000,6 +2040,76 @@ create_hashjoin_path(PlannerInfo *root, } /* + * create_customjoin_path + * Creates a pathnode corresponding to a join between two relations based + * on custom logic provided by extension. + * + * 'joinrel' is the join relation + * 'jointype' is the type of join required + * 'rows' is number of estimated rows + * 'startup_cost' is estimated startup cost + * 'total_cost' is estimated total cost + * 'sjinfo' is extra info about the join for selectivity estimation + * 'outer_path' is the cheapest outer path + * 'inner_path' is the cheapest inner path + * 'restrict_clauses' are the RestrictInfo nodes to apply at the join + * 'pathkeys' are the path keys of the new join path + * 'required_outer' is the set of required outer rels + * 'custom_name' is the name of custom execution provider + * 'custom_flag' is set of CUSTOM_* flags + * 'custom_private' is a list of private datum + */ +CustomJoinPath * +create_customjoin_path(PlannerInfo *root, + RelOptInfo *joinrel, + JoinType jointype, + double rows, + Cost startup_cost, + Cost total_cost, + SpecialJoinInfo *sjinfo, + Path *outer_path, + Path *inner_path, + List *restrict_clauses, + List *pathkeys, + Relids required_outer, + const char *custom_name, + uint32 custom_flags, + List *custom_private) +{ + CustomJoinPath *pathnode = makeNode(CustomJoinPath); + + pathnode->jpath.path.pathtype = T_CustomJoin; + pathnode->jpath.path.parent = joinrel; + pathnode->jpath.path.param_info + = get_joinrel_parampathinfo(root, + joinrel, + outer_path, + inner_path, + sjinfo, + required_outer, + &restrict_clauses); + /* + * Unlike other create_XXXjoin_path routines, it is caller's job to + * calculate adequate cost estimation for this custom join logic. + */ + pathnode->jpath.path.rows = rows; + pathnode->jpath.path.startup_cost = startup_cost; + pathnode->jpath.path.total_cost = total_cost; + + pathnode->jpath.path.pathkeys = pathkeys; + pathnode->jpath.jointype = jointype; + pathnode->jpath.outerjoinpath = outer_path; + pathnode->jpath.innerjoinpath = inner_path; + pathnode->jpath.joinrestrictinfo = restrict_clauses; + + pathnode->custom_name = pstrdup(custom_name); + pathnode->custom_flags = custom_flags; + pathnode->custom_private = custom_private; + + return pathnode; +} + +/* * reparameterize_path * Attempt to modify a Path to have greater parameterization * diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 3b430e0..ecf816c 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1175,6 +1175,24 @@ typedef struct BitmapOrState int nplans; /* number of input plans */ } BitmapOrState; +/* ---------------- + * CustomPlanState information + * + * CustomPlan is used to implement custom behavior but expect for scan + * and join. + */ +typedef struct CustomPlanState +{ + PlanState ps; + /* use struct pointer to avoid including custom.h here */ + struct CustomProvider *custom_provider; + uint custom_flags; + void *custom_state; + /* special handling if multiple subplans are here, as Append */ + PlanState **custom_subplans; + int custom_numplans; +} CustomPlanState; + /* ---------------------------------------------------------------- * Scan State Information * ---------------------------------------------------------------- @@ -1494,6 +1512,20 @@ typedef struct ForeignScanState void *fdw_state; /* foreign-data wrapper can keep state here */ } ForeignScanState; +/* ---------------- + * CustomScanState information + * + * CustomScan nodes are used to scan relation based on custom logic. + */ +typedef struct CustomScanState +{ + ScanState ss; + /* use struct pointer to avoid including nodeCustom.h here */ + struct CustomProvider *custom_provider; + uint custom_flags; + void *custom_state; +} CustomScanState; + /* ---------------------------------------------------------------- * Join State Information * ---------------------------------------------------------------- @@ -1626,6 +1658,19 @@ typedef struct HashJoinState bool hj_OuterNotEmpty; } HashJoinState; +/* ---------------- + * CustomJoinState information + * + * CustomJoin nodes are used to join relations based on custom logic + */ +typedef struct CustomJoinState +{ + JoinState js; + /* use struct pointer to avoid including nodeCustom.h here */ + struct CustomProvider *custom_provider; + uint custom_flags; + void *custom_state; +} CustomJoinState; /* ---------------------------------------------------------------- * Materialization State Information diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 78368c6..d04eccd 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -76,6 +76,9 @@ typedef enum NodeTag T_SetOp, T_LockRows, T_Limit, + T_CustomPlan, + T_CustomScan, + T_CustomJoin, /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, @@ -121,6 +124,9 @@ typedef enum NodeTag T_SetOpState, T_LockRowsState, T_LimitState, + T_CustomPlanState, + T_CustomScanState, + T_CustomJoinState, /* * TAGS FOR PRIMITIVE NODES (primnodes.h) @@ -229,6 +235,9 @@ typedef enum NodeTag T_ResultPath, T_MaterialPath, T_UniquePath, + T_CustomPlanPath, + T_CustomScanPath, + T_CustomJoinPath, T_EquivalenceClass, T_EquivalenceMember, T_PathKey, diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 44ea0b7..aa12205 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -483,6 +483,18 @@ typedef struct ForeignScan bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; +/* ---------------- + * CustomScan node + * + */ +typedef struct CustomScan +{ + Scan scan; + const char *custom_name; + uint custom_flags; + List *custom_private; + List *custom_exprs; +} CustomScan; /* * ========== @@ -570,6 +582,19 @@ typedef struct HashJoin } HashJoin; /* ---------------- + * CustomJoin node + * + */ +typedef struct CustomJoin +{ + Join join; + const char *custom_name; + uint custom_flags; + List *custom_private; + List *custom_exprs; +} CustomJoin; + +/* ---------------- * materialization node * ---------------- */ @@ -750,6 +775,19 @@ typedef struct Limit Node *limitCount; /* COUNT parameter, or NULL if none */ } Limit; +/* + * CustomPlan node + * + */ +typedef struct CustomPlan +{ + Plan plan; + const char *custom_name; + uint custom_flags; + List *custom_private; + List *custom_exprs; + List *custom_subplans; /* if here is multiple subplans */ +} CustomPlan; /* * RowMarkType - diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index a2853fb..4a3c924 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1036,6 +1036,30 @@ typedef struct HashPath } HashPath; /* + * CustomScanPath + * + */ +typedef struct CustomScanPath +{ + Path path; + const char *custom_name; + uint32 custom_flags; + List *custom_private; +} CustomScanPath; + +/* + * CustomJoinPath + * + */ +typedef struct CustomJoinPath +{ + JoinPath jpath; + const char *custom_name; + uint32 custom_flags; + List *custom_private; +} CustomJoinPath; + +/* * Restriction clause info. * * We create one of these for each AND sub-clause of a restriction condition diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 9686229..735dbec 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -82,6 +82,16 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer, List *fdw_private); +extern CustomScanPath *create_customscan_path(PlannerInfo *root, + RelOptInfo *baserel, + double rows, + Cost startup_cost, + Cost total_cost, + List *pathkeys, + Relids required_outer, + const char *custom_name, + uint32 custom_flags, + List *custom_private); extern Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path); extern Relids calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path); @@ -124,6 +134,22 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root, Relids required_outer, List *hashclauses); +extern CustomJoinPath *create_customjoin_path(PlannerInfo *root, + RelOptInfo *joinrel, + JoinType jointype, + double rows, + Cost startup_cost, + Cost total_cost, + SpecialJoinInfo *sjinfo, + Path *outer_path, + Path *inner_path, + List *restrict_clauses, + List *pathkeys, + Relids required_outer, + const char *custom_name, + uint32 custom_flags, + List *custom_private); + extern Path *reparameterize_path(PlannerInfo *root, Path *path, Relids required_outer, double loop_count); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 9ef93c7..e9aa199 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -29,6 +29,31 @@ typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root, List *initial_rels); extern PGDLLIMPORT join_search_hook_type join_search_hook; +/* Hook for plugins to add custom scan path, in addition to default ones */ +typedef void (*add_scan_path_hook_type)(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); +extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook; + +#define add_custom_scan_paths(root,baserel,rte) \ + do { \ + if (add_scan_path_hook) \ + (*add_scan_path_hook)((root),(baserel),(rte)); \ + } while(0) + +/* Hook for plugins to add custom join path, in addition to default ones */ +typedef void (*add_join_path_hook_type)(PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + List *restrictlist, + List *mergeclause_list, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels, + Relids extra_lateral_rels); +extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook; extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist); extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,