diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f11e023c0b26..69bbb8dce7d7 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1970,6 +1970,8 @@ pathtarget_contains_rowidexpr(PathTarget *pathtarget) return false; } +static bool is_function_scan_on_initplan(PlannerInfo *root, Path *best_path); + /* * create_projection_plan * @@ -2026,8 +2028,8 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) * Tell create_plan_recurse that we're going to ignore the tlist it * produces. */ - subplan = create_plan_recurse(root, best_path->subpath, - CP_IGNORE_TLIST); + int subplan_flags = is_function_scan_on_initplan(root, best_path->subpath) ? 0 : CP_IGNORE_TLIST; + subplan = create_plan_recurse(root, best_path->subpath, subplan_flags); Assert(is_projection_capable_plan(subplan)); tlist = build_path_tlist(root, &best_path->path); } @@ -8205,6 +8207,65 @@ cdbpathtoplan_create_motion_plan(PlannerInfo *root, return motion; } /* cdbpathtoplan_create_motion_plan */ +/* + * is_function_scan_on_initplan + * + * CDB: gpdb specific function to check if a function scan should be executed on an initplan. + */ +static bool +is_function_scan_on_initplan(PlannerInfo *root, Path *best_path) { + Index scan_relid = best_path->parent->relid; + RangeTblEntry *rte; + RangeTblFunction *rtfunc; + FuncExpr *funcexpr; + char exec_location; + + if (best_path->pathtype != T_FunctionScan) + return false; + + /* + * In utility mode (or when planning a local query in QE), ignore EXECUTE + * ON markings and run the function the normal way. + */ + if (Gp_role != GP_ROLE_DISPATCH) + return false; + + /* Current function scan is already in an initplan, do nothing. */ + if (!get_allow_append_initplan_for_function_scan()) + return false; + + /* + * If INITPLAN function is executed on QD, there is no + * need to add additional initplan to run this function. + * Recall that the reason to introduce INITPLAN function + * is that function runing on QE can not do dispatch. + */ + if (root->curSlice->parentIndex == -1) + return false; + + /* it should be a function base rel... */ + Assert(scan_relid > 0); + rte = planner_rt_fetch(scan_relid, root); + Assert(rte->rtekind == RTE_FUNCTION); + + /* Currently we limit function number to one */ + if (list_length(rte->functions) != 1) + return false; + + rtfunc = (RangeTblFunction *) linitial(rte->functions); + + if (!IsA(rtfunc->funcexpr, FuncExpr)) + return false; + + /* function must be specified EXECUTE ON INITPLAN */ + funcexpr = (FuncExpr *) rtfunc->funcexpr; + exec_location = func_exec_location(funcexpr->funcid); + if (exec_location != PROEXECLOCATION_INITPLAN) + return false; + + return true; +} + /* * append_initplan_for_function_scan * diff --git a/src/test/regress/expected/function_extensions.out b/src/test/regress/expected/function_extensions.out index 7d390535253e..ed25f94e6ea5 100644 --- a/src/test/regress/expected/function_extensions.out +++ b/src/test/regress/expected/function_extensions.out @@ -577,3 +577,22 @@ select array(select f from hello_initplan() as f); {hello} (1 row) +-- Test INITPLAN functions with projections +DROP TABLE IF EXISTS t8_function_scan; +-- should succeed and add 3 rows with IDs (112, 223, 334) +CREATE TABLE t8_function_scan AS SELECT country_id+1 AS a, country FROM get_country(); +-- should succeed and add 3 rows with IDs (113, 224, 335) +INSERT INTO t8_function_scan SELECT country_id+2 AS a, country FROM get_country(); +-- should have 6 rows without any NULL entries +SELECT * FROM t8_function_scan ORDER BY a; + a | country +-----+--------- + 112 | INDIA + 113 | INDIA + 223 | CANADA + 224 | CANADA + 334 | USA + 335 | USA +(6 rows) + +DROP TABLE t8_function_scan; diff --git a/src/test/regress/expected/function_extensions_optimizer.out b/src/test/regress/expected/function_extensions_optimizer.out index e831bc381e71..1dbc85fa0f84 100644 --- a/src/test/regress/expected/function_extensions_optimizer.out +++ b/src/test/regress/expected/function_extensions_optimizer.out @@ -578,3 +578,22 @@ select array(select f from hello_initplan() as f); {hello} (1 row) +-- Test INITPLAN functions with projections +DROP TABLE IF EXISTS t8_function_scan; +-- should succeed and add 3 rows with IDs (112, 223, 334) +CREATE TABLE t8_function_scan AS SELECT country_id+1 AS a, country FROM get_country(); +-- should succeed and add 3 rows with IDs (113, 224, 335) +INSERT INTO t8_function_scan SELECT country_id+2 AS a, country FROM get_country(); +-- should have 6 rows without any NULL entries +SELECT * FROM t8_function_scan ORDER BY a; + a | country +-----+--------- + 112 | INDIA + 113 | INDIA + 223 | CANADA + 224 | CANADA + 334 | USA + 335 | USA +(6 rows) + +DROP TABLE t8_function_scan; diff --git a/src/test/regress/sql/function_extensions.sql b/src/test/regress/sql/function_extensions.sql index 48940e4c15dd..4a071a7c2ece 100644 --- a/src/test/regress/sql/function_extensions.sql +++ b/src/test/regress/sql/function_extensions.sql @@ -298,5 +298,13 @@ execute on initplan; explain select array(select f from hello_initplan() as f); select array(select f from hello_initplan() as f); - +-- Test INITPLAN functions with projections +DROP TABLE IF EXISTS t8_function_scan; +-- should succeed and add 3 rows with IDs (112, 223, 334) +CREATE TABLE t8_function_scan AS SELECT country_id+1 AS a, country FROM get_country(); +-- should succeed and add 3 rows with IDs (113, 224, 335) +INSERT INTO t8_function_scan SELECT country_id+2 AS a, country FROM get_country(); +-- should have 6 rows without any NULL entries +SELECT * FROM t8_function_scan ORDER BY a; +DROP TABLE t8_function_scan;