summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshutosh Bapat2012-10-11 06:58:02 +0000
committerAshutosh Bapat2012-10-11 06:58:02 +0000
commit7b633f16bf11ade60acef4520c614dc3dd5704b8 (patch)
treefe5495c09b4b51f3a0471244623450257f795b1d
parent5ccf23fc829af560f4ddb325e0a6f5eacfdfd10c (diff)
Move Postgres-XC specific planner code to appropriate PostgreSQL directories.
-rw-r--r--src/backend/catalog/pg_proc.c1
-rw-r--r--src/backend/commands/schemacmds.c2
-rw-r--r--src/backend/nodes/copyfuncs.c2
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/optimizer/path/pgxcpath.c2
-rw-r--r--src/backend/optimizer/plan/pgxcplan.c818
-rw-r--r--src/backend/optimizer/plan/planner.c2
-rw-r--r--src/backend/optimizer/plan/setrefs.c2
-rw-r--r--src/backend/parser/analyze.c2
-rw-r--r--src/backend/parser/parse_utilcmd.c2
-rw-r--r--src/backend/pgxc/Makefile2
-rw-r--r--src/backend/pgxc/plan/Makefile19
-rw-r--r--src/backend/pgxc/plan/planner.c884
-rw-r--r--src/backend/tcop/postgres.c2
-rw-r--r--src/backend/tcop/pquery.c2
-rw-r--r--src/backend/tcop/utility.c2
-rw-r--r--src/backend/utils/adt/ruleutils.c2
-rw-r--r--src/backend/utils/misc/guc.c2
-rw-r--r--src/include/optimizer/pgxcplan.h (renamed from src/include/pgxc/planner.h)13
-rw-r--r--src/include/pgxc/execRemote.h2
20 files changed, 833 insertions, 932 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 89b1027951..c758f63224 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -39,7 +39,6 @@
#ifdef PGXC
#include "pgxc/execRemote.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
#endif
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 0c7152398a..6cc7cee3cf 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -32,7 +32,7 @@
#ifdef PGXC
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c25e197dab..9db6c2b46a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -28,7 +28,7 @@
#include "nodes/relation.h"
#ifdef PGXC
#include "pgxc/locator.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
#include "utils/datum.h"
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 94f0a965a4..8e858316fe 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -28,7 +28,7 @@
#include "nodes/relation.h"
#include "utils/datum.h"
#ifdef PGXC
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
diff --git a/src/backend/optimizer/path/pgxcpath.c b/src/backend/optimizer/path/pgxcpath.c
index 9118315e75..e51d1b3359 100644
--- a/src/backend/optimizer/path/pgxcpath.c
+++ b/src/backend/optimizer/path/pgxcpath.c
@@ -20,7 +20,7 @@
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
static RemoteQueryPath *pgxc_find_remotequery_path(RelOptInfo *rel);
static ExecNodes *pgxc_is_join_reducible(ExecNodes *inner_en, ExecNodes *outer_en,
diff --git a/src/backend/optimizer/plan/pgxcplan.c b/src/backend/optimizer/plan/pgxcplan.c
index 76ba362f78..97ec9f183e 100644
--- a/src/backend/optimizer/plan/pgxcplan.c
+++ b/src/backend/optimizer/plan/pgxcplan.c
@@ -24,23 +24,39 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
+#include "optimizer/pgxcplan.h"
#include "optimizer/pgxcship.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
+#include "parser/parse_func.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/syscache.h"
-
+/* fast query shipping is enabled by default */
+bool enable_fast_query_shipping = true;
+
+static void validate_part_col_updatable(const Query *query);
+static bool contains_temp_tables(List *rtable);
+static void pgxc_handle_unsupported_stmts(Query *query);
+static PlannedStmt *pgxc_FQS_planner(Query *query, int cursorOptions,
+ ParamListInfo boundParams);
+static PlannedStmt *pgxc_handle_exec_direct(Query *query, int cursorOptions,
+ ParamListInfo boundParams);
+static RemoteQuery *pgxc_FQS_create_remote_plan(Query *query,
+ ExecNodes *exec_nodes,
+ bool is_exec_direct);
+static void pgxc_set_remote_parameters(PlannedStmt *plan, ParamListInfo boundParams);
static void pgxc_locate_grouping_columns(PlannerInfo *root, List *tlist,
AttrNumber *grpColIdx);
static List *pgxc_process_grouping_targetlist(PlannerInfo *root,
@@ -59,6 +75,7 @@ static Query *pgxc_build_shippable_query_recurse(PlannerInfo *root,
static RemoteQuery *make_remotequery(List *qptlist, List *qpqual,
Index scanrelid);
static RangeTblEntry *make_dummy_remote_rte(char *relname, Alias *alias);
+static void pgxc_rqplan_adjust_vars(RemoteQuery *rqplan, Node *node);
/*
* pgxc_separate_quals
@@ -468,7 +485,7 @@ pgxc_build_shippable_query_recurse(PlannerInfo *root, RemoteQueryPath *rqpath,
* rqplan->remote_query. We don't need a expression mutator here, since we are
* replacing scalar members in the Var nodes.
*/
-extern void
+static void
pgxc_rqplan_adjust_vars(RemoteQuery *rqplan, Node *node)
{
List *var_list = pull_var_clause(node, PVC_RECURSE_AGGREGATES,
@@ -1998,3 +2015,798 @@ make_dummy_remote_rte(char *relname, Alias *alias)
return dummy_rte;
}
+
+/*
+ * make_ctid_col_ref
+ *
+ * creates a Var for a column referring to ctid
+ */
+
+static Var *
+make_ctid_col_ref(Query *qry)
+{
+ ListCell *lc1, *lc2;
+ RangeTblEntry *rte1, *rte2;
+ int tableRTEs, firstTableRTENumber;
+ RangeTblEntry *rte_in_query = NULL;
+ AttrNumber attnum;
+ Oid vartypeid;
+ int32 type_mod;
+ Oid varcollid;
+
+ /*
+ * If the query has more than 1 table RTEs where both are different, we can not add ctid to the query target list
+ * We should in this case skip adding it to the target list and a WHERE CURRENT OF should then
+ * fail saying the query is not a simply update able scan of table
+ */
+
+ tableRTEs = 0;
+ foreach(lc1, qry->rtable)
+ {
+ rte1 = (RangeTblEntry *) lfirst(lc1);
+
+ if (rte1->rtekind == RTE_RELATION)
+ {
+ tableRTEs++;
+ if (tableRTEs > 1)
+ {
+ /*
+ * See if we get two RTEs in case we have two references
+ * to the same table with different aliases
+ */
+ foreach(lc2, qry->rtable)
+ {
+ rte2 = (RangeTblEntry *) lfirst(lc2);
+
+ if (rte2->rtekind == RTE_RELATION)
+ {
+ if (rte2->relid != rte1->relid)
+ {
+ return NULL;
+ }
+ }
+ }
+ continue;
+ }
+ rte_in_query = rte1;
+ }
+ }
+
+ if (tableRTEs > 1)
+ {
+ firstTableRTENumber = 0;
+ foreach(lc1, qry->rtable)
+ {
+ rte1 = (RangeTblEntry *) lfirst(lc1);
+ firstTableRTENumber++;
+ if (rte1->rtekind == RTE_RELATION)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ firstTableRTENumber = 1;
+ }
+
+ attnum = specialAttNum("ctid");
+ Assert(rte_in_query);
+ get_rte_attribute_type(rte_in_query, attnum, &vartypeid, &type_mod, &varcollid);
+ return makeVar(firstTableRTENumber, attnum, vartypeid, type_mod, varcollid, 0);
+}
+
+
+/*
+ * Returns true if at least one temporary table is in use
+ * in query (and its subqueries)
+ */
+static bool
+contains_temp_tables(List *rtable)
+{
+ ListCell *item;
+
+ foreach(item, rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(item);
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ if (IsTempTable(rte->relid))
+ return true;
+ }
+ else if (rte->rtekind == RTE_SUBQUERY &&
+ contains_temp_tables(rte->subquery->rtable))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * get_plan_combine_type - determine combine type
+ *
+ * COMBINE_TYPE_SAME - for replicated updates
+ * COMBINE_TYPE_SUM - for hash and round robin updates
+ * COMBINE_TYPE_NONE - for operations where row_count is not applicable
+ *
+ * return NULL if it is not safe to be done in a single step.
+ */
+static CombineType
+get_plan_combine_type(Query *query, char baselocatortype)
+{
+
+ switch (query->commandType)
+ {
+ case CMD_INSERT:
+ case CMD_UPDATE:
+ case CMD_DELETE:
+ return baselocatortype == LOCATOR_TYPE_REPLICATED ?
+ COMBINE_TYPE_SAME : COMBINE_TYPE_SUM;
+
+ default:
+ return COMBINE_TYPE_NONE;
+ }
+ /* quiet compiler warning */
+ return COMBINE_TYPE_NONE;
+}
+
+/*
+ * get oid of the function whose name is passed as argument
+ */
+
+static Oid
+get_fn_oid(char *fn_name, Oid *p_rettype)
+{
+ Value *fn_nm;
+ List *fn_name_list;
+ FuncDetailCode fdc;
+ bool retset;
+ int nvargs;
+ Oid *true_typeids;
+ Oid func_oid;
+
+ fn_nm = makeString(fn_name);
+ fn_name_list = list_make1(fn_nm);
+
+ fdc = func_get_detail(fn_name_list,
+ NULL, /* argument expressions */
+ NULL, /* argument names */
+ 0, /* argument numbers */
+ NULL, /* argument types */
+ false, /* expand variable number or args */
+ false, /* expand defaults */
+ &func_oid, /* oid of the function - returned detail*/
+ p_rettype, /* function return type - returned detail */
+ &retset, /* - returned detail*/
+ &nvargs, /* - returned detail*/
+ &true_typeids, /* - returned detail */
+ NULL /* arguemnt defaults returned*/
+ );
+
+ pfree(fn_name_list);
+ if (fdc == FUNCDETAIL_NORMAL)
+ {
+ return func_oid;
+ }
+ return InvalidOid;
+}
+
+/*
+ * Append ctid to the field list of step queries to support update
+ * WHERE CURRENT OF. The ctid is not sent down to client but used as a key
+ * to find target tuple.
+ * PGXCTODO: Bug
+ * This function modifies the original query to add ctid
+ * and nodename in the targetlist. It should rather modify the targetlist of the
+ * query to be shipped by the RemoteQuery node.
+ */
+static void
+fetch_ctid_of(Plan *subtree, Query *query)
+{
+ /* recursively process subnodes */
+ if (innerPlan(subtree))
+ fetch_ctid_of(innerPlan(subtree), query);
+ if (outerPlan(subtree))
+ fetch_ctid_of(outerPlan(subtree), query);
+
+ /* we are only interested in RemoteQueries */
+ if (IsA(subtree, RemoteQuery))
+ {
+ RemoteQuery *step = (RemoteQuery *) subtree;
+ TargetEntry *te1;
+ Query *temp_qry;
+ FuncExpr *func_expr;
+ AttrNumber resno;
+ Oid funcid;
+ Oid rettype;
+ Var *ctid_expr;
+ MemoryContext oldcontext;
+ MemoryContext tmpcontext;
+
+ tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
+ "Temp Context",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(tmpcontext);
+
+ /* Copy the query tree to make changes to the target list */
+ temp_qry = copyObject(query);
+ /* Get the number of entries in the target list */
+ resno = list_length(temp_qry->targetList);
+
+ /* Make a ctid column ref expr to add in target list */
+ ctid_expr = make_ctid_col_ref(temp_qry);
+ if (ctid_expr == NULL)
+ {
+ MemoryContextSwitchTo(oldcontext);
+ MemoryContextDelete(tmpcontext);
+ return;
+ }
+
+ te1 = makeTargetEntry((Expr *)ctid_expr, resno+1, NULL, false);
+
+ /* add the target entry to the query target list */
+ temp_qry->targetList = lappend(temp_qry->targetList, te1);
+
+ /* PGXCTODO We can take this call in initialization rather than getting it always */
+
+ /* Get the Oid of the function */
+ funcid = get_fn_oid("pgxc_node_str", &rettype);
+ if (OidIsValid(funcid))
+ {
+ StringInfoData deparsed_qry;
+ TargetEntry *te2;
+
+ /* create a function expression */
+ func_expr = makeFuncExpr(funcid, rettype, NULL, InvalidOid, InvalidOid, COERCE_DONTCARE);
+ /* make a target entry for function call */
+ te2 = makeTargetEntry((Expr *)func_expr, resno+2, NULL, false);
+ /* add the target entry to the query target list */
+ temp_qry->targetList = lappend(temp_qry->targetList, te2);
+
+ initStringInfo(&deparsed_qry);
+ deparse_query(temp_qry, &deparsed_qry, NIL);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ if (step->sql_statement != NULL)
+ pfree(step->sql_statement);
+
+ step->sql_statement = pstrdup(deparsed_qry.data);
+
+ MemoryContextDelete(tmpcontext);
+ }
+ else
+ {
+ MemoryContextSwitchTo(oldcontext);
+ MemoryContextDelete(tmpcontext);
+ }
+ }
+}
+
+/*
+ * Build up a QueryPlan to execute on.
+ *
+ * This functions tries to find out whether
+ * 1. The statement can be shipped to the Datanode and Coordinator is needed
+ * only as a proxy - in which case, it creates a single node plan.
+ * 2. The statement can be evaluated on the Coordinator completely - thus no
+ * query shipping is involved and standard_planner() is invoked to plan the
+ * statement
+ * 3. The statement needs Coordinator as well as Datanode for evaluation -
+ * again we use standard_planner() to plan the statement.
+ *
+ * The plan generated in either of the above cases is returned.
+ */
+PlannedStmt *
+pgxc_planner(Query *query, int cursorOptions, ParamListInfo boundParams)
+{
+ PlannedStmt *result;
+
+ /* handle the un-supported statements, obvious errors etc. */
+ pgxc_handle_unsupported_stmts(query);
+
+ result = pgxc_handle_exec_direct(query, cursorOptions, boundParams);
+ if (result)
+ return result;
+
+ /* see if can ship the query completely */
+ result = pgxc_FQS_planner(query, cursorOptions, boundParams);
+ if (result)
+ return result;
+
+ /* we need Coordinator for evaluation, invoke standard planner */
+ result = standard_planner(query, cursorOptions, boundParams);
+ pgxc_set_remote_parameters(result, boundParams);
+ return result;
+}
+
+static PlannedStmt *
+pgxc_handle_exec_direct(Query *query, int cursorOptions,
+ ParamListInfo boundParams)
+{
+ PlannedStmt *result = NULL;
+ PlannerGlobal *glob;
+ PlannerInfo *root;
+ /*
+ * if the query has its utility set, it could be an EXEC_DIRECT statement,
+ * check if it needs to be executed on Coordinator
+ */
+ if (query->utilityStmt &&
+ IsA(query->utilityStmt, RemoteQuery))
+ {
+ RemoteQuery *node = (RemoteQuery *)query->utilityStmt;
+ /* EXECUTE DIRECT statements on remote nodes don't need Coordinator */
+ if (node->exec_direct_type != EXEC_DIRECT_NONE &&
+ node->exec_direct_type != EXEC_DIRECT_LOCAL &&
+ node->exec_direct_type != EXEC_DIRECT_LOCAL_UTILITY)
+ {
+ glob = makeNode(PlannerGlobal);
+ glob->boundParams = boundParams;
+ /* Create a PlannerInfo data structure, usually it is done for a subquery */
+ root = makeNode(PlannerInfo);
+ root->parse = query;
+ root->glob = glob;
+ root->query_level = 1;
+ root->planner_cxt = CurrentMemoryContext;
+ /* build the PlannedStmt result */
+ result = makeNode(PlannedStmt);
+ /* Try and set what we can, rest must have been zeroed out by makeNode() */
+ result->commandType = query->commandType;
+ result->canSetTag = query->canSetTag;
+ /* Set result relations */
+ if (query->commandType != CMD_SELECT)
+ result->resultRelations = list_make1_int(query->resultRelation);
+
+ result->planTree = (Plan *)pgxc_FQS_create_remote_plan(query, NULL, true);
+ result->rtable = query->rtable;
+ /*
+ * We need to save plan dependencies, so that dropping objects will
+ * invalidate the cached plan if it depends on those objects. Table
+ * dependencies are available in glob->relationOids and all other
+ * dependencies are in glob->invalItems. These fields can be retrieved
+ * through set_plan_references().
+ */
+ result->planTree = set_plan_references(root, result->planTree);
+ result->relationOids = glob->relationOids;
+ result->invalItems = glob->invalItems;
+ }
+ }
+
+ /* Set existing remote parameters */
+ pgxc_set_remote_parameters(result, boundParams);
+
+ return result;
+}
+/*
+ * pgxc_handle_unsupported_stmts
+ * Throw error for the statements that can not be handled in XC
+ */
+static void
+pgxc_handle_unsupported_stmts(Query *query)
+{
+ /*
+ * PGXCTODO: This validation will not be removed
+ * until we support moving tuples from one node to another
+ * when the partition column of a table is updated
+ */
+ if (query->commandType == CMD_UPDATE)
+ validate_part_col_updatable(query);
+
+ if (query->returningList)
+ ereport(ERROR,
+ (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
+ (errmsg("RETURNING clause not yet supported"))));
+}
+
+/*
+ * pgxc_FQS_planner
+ * The routine tries to see if the statement can be completely evaluated on the
+ * Datanodes. In such cases Coordinator is not needed to evaluate the statement,
+ * and just acts as a proxy. A statement can be completely shipped to the remote
+ * node if every row of the result can be evaluated on a single Datanode.
+ * For example:
+ *
+ * 1. SELECT * FROM tab1; where tab1 is a distributed table - Every row of the
+ * result set can be evaluated at a single Datanode. Hence this statement is
+ * completely shippable even though many Datanodes are involved in evaluating
+ * complete result set. In such case Coordinator will be able to gather rows
+ * arisign from individual Datanodes and proxy the result to the client.
+ *
+ * 2. SELECT count(*) FROM tab1; where tab1 is a distributed table - there is
+ * only one row in the result but it needs input from all the Datanodes. Hence
+ * this is not completely shippable.
+ *
+ * 3. SELECT count(*) FROM tab1; where tab1 is replicated table - since result
+ * can be obtained from a single Datanode, this is a completely shippable
+ * statement.
+ *
+ * fqs in the name of function is acronym for fast query shipping.
+ */
+static PlannedStmt *
+pgxc_FQS_planner(Query *query, int cursorOptions, ParamListInfo boundParams)
+{
+ PlannedStmt *result;
+ PlannerGlobal *glob;
+ PlannerInfo *root;
+ ExecNodes *exec_nodes;
+ Plan *top_plan;
+
+ /* Try by-passing standard planner, if fast query shipping is enabled */
+ if (!enable_fast_query_shipping)
+ return NULL;
+
+ /* Cursor options may come from caller or from DECLARE CURSOR stmt */
+ if (query->utilityStmt &&
+ IsA(query->utilityStmt, DeclareCursorStmt))
+ cursorOptions |= ((DeclareCursorStmt *) query->utilityStmt)->options;
+ /*
+ * If the query can not be or need not be shipped to the Datanodes, don't
+ * create any plan here. standard_planner() will take care of it.
+ */
+ exec_nodes = pgxc_is_query_shippable(query, 0);
+ if (exec_nodes == NULL)
+ return NULL;
+
+ glob = makeNode(PlannerGlobal);
+ glob->boundParams = boundParams;
+ /* Create a PlannerInfo data structure, usually it is done for a subquery */
+ root = makeNode(PlannerInfo);
+ root->parse = query;
+ root->glob = glob;
+ root->query_level = 1;
+ root->planner_cxt = CurrentMemoryContext;
+
+ /*
+ * We decided to ship the query to the Datanode/s, create a RemoteQuery node
+ * for the same.
+ */
+ top_plan = (Plan *)pgxc_FQS_create_remote_plan(query, exec_nodes, false);
+ /*
+ * If creating a plan for a scrollable cursor, make sure it can run
+ * backwards on demand. Add a Material node at the top at need.
+ */
+ if (cursorOptions & CURSOR_OPT_SCROLL)
+ {
+ if (!ExecSupportsBackwardScan(top_plan))
+ top_plan = materialize_finished_plan(top_plan);
+ }
+
+ /*
+ * Just before creating the PlannedStmt, do some final cleanup
+ * We need to save plan dependencies, so that dropping objects will
+ * invalidate the cached plan if it depends on those objects. Table
+ * dependencies are available in glob->relationOids and all other
+ * dependencies are in glob->invalItems. These fields can be retrieved
+ * through set_plan_references().
+ */
+ top_plan = set_plan_references(root, top_plan);
+
+ /* build the PlannedStmt result */
+ result = makeNode(PlannedStmt);
+ /* Try and set what we can, rest must have been zeroed out by makeNode() */
+ result->commandType = query->commandType;
+ result->canSetTag = query->canSetTag;
+ result->utilityStmt = query->utilityStmt;
+
+ /* Set result relations */
+ if (query->commandType != CMD_SELECT)
+ result->resultRelations = list_make1_int(query->resultRelation);
+ result->planTree = top_plan;
+ result->rtable = query->rtable;
+ result->relationOids = glob->relationOids;
+ result->invalItems = glob->invalItems;
+
+ /*
+ * If query is DECLARE CURSOR fetch CTIDs and node names from the remote node
+ * Use CTID as a key to update/delete tuples on remote nodes when handling
+ * WHERE CURRENT OF.
+ */
+ if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
+ fetch_ctid_of(result->planTree, query);
+
+ /* Set existing remote parameters */
+ pgxc_set_remote_parameters(result, boundParams);
+
+ return result;
+}
+
+static RemoteQuery *
+pgxc_FQS_create_remote_plan(Query *query, ExecNodes *exec_nodes, bool is_exec_direct)
+{
+ RemoteQuery *query_step;
+ StringInfoData buf;
+ RangeTblEntry *dummy_rte;
+
+ /* EXECUTE DIRECT statements have their RemoteQuery node already built when analyzing */
+ if (is_exec_direct)
+ {
+ Assert(IsA(query->utilityStmt, RemoteQuery));
+ query_step = (RemoteQuery *)query->utilityStmt;
+ query->utilityStmt = NULL;
+ }
+ else
+ {
+ query_step = makeNode(RemoteQuery);
+ query_step->combine_type = COMBINE_TYPE_NONE;
+ query_step->exec_type = EXEC_ON_DATANODES;
+ query_step->exec_direct_type = EXEC_DIRECT_NONE;
+ query_step->exec_nodes = exec_nodes;
+ }
+
+ Assert(query_step->exec_nodes);
+
+ /* Datanodes should finalise the results of this query */
+ query->qry_finalise_aggs = true;
+
+ /* Deparse query tree to get step query. */
+ if (query_step->sql_statement == NULL)
+ {
+ initStringInfo(&buf);
+ deparse_query(query, &buf, NIL);
+ query_step->sql_statement = pstrdup(buf.data);
+ pfree(buf.data);
+ }
+ /*
+ * PGXCTODO: we may route this same Query structure through
+ * standard_planner, where we don't want Datanodes to finalise the results.
+ * Turn it off. At some point, we will avoid routing the same query
+ * structure through the standard_planner by modifying it only when it's not
+ * be routed through standard_planner.
+ */
+ query->qry_finalise_aggs = false;
+ /* Optimize multi-node handling */
+ query_step->read_only = (query->commandType == CMD_SELECT && !query->hasForUpdate);
+ query_step->has_row_marks = query->hasForUpdate;
+
+ /* Check if temporary tables are in use in query */
+ /* PGXC_FQS_TODO: scanning the rtable again for the queries should not be
+ * needed. We should be able to find out if the query has a temporary object
+ * while finding nodes for the objects. But there is no way we can convey
+ * that information here. Till such a connection is available, this is it.
+ */
+ if (contains_temp_tables(query->rtable))
+ query_step->is_temp = true;
+
+ /*
+ * We need to evaluate some expressions like the ExecNodes->en_expr at
+ * Coordinator, prepare those for evaluation. Ideally we should call
+ * preprocess_expression, but it needs PlannerInfo structure for the same
+ */
+ fix_opfuncids((Node *)(query_step->exec_nodes->en_expr));
+ /*
+ * PGXCTODO
+ * When Postgres runs insert into t (a) values (1); against table
+ * defined as create table t (a int, b int); the plan is looking
+ * like insert into t (a,b) values (1,null);
+ * Later executor is verifying plan, to make sure table has not
+ * been altered since plan has been created and comparing table
+ * definition with plan target list and output error if they do
+ * not match.
+ * I could not find better way to generate targetList for pgxc plan
+ * then call standard planner and take targetList from the plan
+ * generated by Postgres.
+ */
+ query_step->combine_type = get_plan_combine_type(
+ query, query_step->exec_nodes->baselocatortype);
+
+ /*
+ * Create a dummy RTE for the remote query being created. Append the dummy
+ * range table entry to the range table. Note that this modifies the master
+ * copy the caller passed us, otherwise e.g EXPLAIN VERBOSE will fail to
+ * find the rte the Vars built below refer to. Also create the tuple
+ * descriptor for the result of this query from the base_tlist (targetlist
+ * we used to generate the remote node query).
+ */
+ dummy_rte = makeNode(RangeTblEntry);
+ dummy_rte->rtekind = RTE_REMOTE_DUMMY;
+ /* Use a dummy relname... */
+ if (is_exec_direct)
+ dummy_rte->relname = "__EXECUTE_DIRECT__";
+ else
+ dummy_rte->relname = "__REMOTE_FQS_QUERY__";
+ dummy_rte->eref = makeAlias("__REMOTE_FQS_QUERY__", NIL);
+ /* Rest will be zeroed out in makeNode() */
+
+ query->rtable = lappend(query->rtable, dummy_rte);
+ query_step->scan.scanrelid = list_length(query->rtable);
+ query_step->scan.plan.targetlist = query->targetList;
+ query_step->base_tlist = query->targetList;
+
+ return query_step;
+}
+
+
+/*
+ * validate whether partition column of a table is being updated
+ */
+static void
+validate_part_col_updatable(const Query *query)
+{
+ RangeTblEntry *rte;
+ RelationLocInfo *rel_loc_info;
+ ListCell *lc;
+
+ /* Make sure there is one table at least */
+ if (query->rtable == NULL)
+ return;
+
+ rte = (RangeTblEntry *) list_nth(query->rtable, query->resultRelation - 1);
+
+
+ if (rte != NULL && rte->relkind != RELKIND_RELATION)
+ /* Bad relation type */
+ return;
+
+ /* See if we have the partitioned case. */
+ rel_loc_info = GetRelationLocInfo(rte->relid);
+
+ /* Any column updation on local relations is fine */
+ if (!rel_loc_info)
+ return;
+
+
+ /* Only LOCATOR_TYPE_HASH & LOCATOR_TYPE_MODULO should be checked */
+ if ( (rel_loc_info->partAttrName != NULL) &&
+ ( (rel_loc_info->locatorType == LOCATOR_TYPE_HASH) || (rel_loc_info->locatorType == LOCATOR_TYPE_MODULO) ) )
+ {
+ /* It is a partitioned table, check partition column in targetList */
+ foreach(lc, query->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->resjunk)
+ continue;
+
+ /*
+ * See if we have a constant expression comparing against the
+ * designated partitioned column
+ */
+ if (strcmp(tle->resname, rel_loc_info->partAttrName) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ (errmsg("Partition column can't be updated in current version"))));
+ }
+ }
+}
+
+/*
+ * AddRemoteQueryNode
+ *
+ * Add a Remote Query node to launch on Datanodes.
+ * This can only be done for a query a Top Level to avoid
+ * duplicated queries on Datanodes.
+ */
+List *
+AddRemoteQueryNode(List *stmts, const char *queryString, RemoteQueryExecType remoteExecType, bool is_temp)
+{
+ List *result = stmts;
+
+ /* If node is appplied on EXEC_ON_NONE, simply return the list unchanged */
+ if (remoteExecType == EXEC_ON_NONE)
+ return result;
+
+ /* Only a remote Coordinator is allowed to send a query to backend nodes */
+ if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
+ {
+ RemoteQuery *step = makeNode(RemoteQuery);
+ step->combine_type = COMBINE_TYPE_SAME;
+ step->sql_statement = (char *) queryString;
+ step->exec_type = remoteExecType;
+ step->is_temp = is_temp;
+ result = lappend(result, step);
+ }
+
+ return result;
+}
+
+/*
+ * pgxc_query_contains_temp_tables
+ *
+ * Check if there is any temporary object used in given list of queries.
+ */
+bool
+pgxc_query_contains_temp_tables(List *queries)
+{
+ ListCell *elt;
+
+ foreach(elt, queries)
+ {
+ Query *query = (Query *) lfirst(elt);
+
+ if (!query)
+ continue;
+
+ switch(query->commandType)
+ {
+ case CMD_SELECT:
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ if (contains_temp_tables(query->rtable))
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * pgxc_query_contains_utility
+ *
+ * Check if there is any utility statement in given list of queries.
+ */
+bool
+pgxc_query_contains_utility(List *queries)
+{
+ ListCell *elt;
+
+ foreach(elt, queries)
+ {
+ Query *query = (Query *) lfirst(elt);
+
+ if (!query)
+ continue;
+
+ if (query->commandType == CMD_UTILITY)
+ return true;
+ }
+
+ return false;
+}
+
+
+/*
+ * pgxc_set_remote_parameters
+ *
+ * Set the list of remote parameters for remote plan
+ */
+static void
+pgxc_set_remote_parameters(PlannedStmt *plan, ParamListInfo boundParams)
+{
+ Oid *param_types;
+ int cntParam, i;
+
+ /* Leave if no plan */
+ if (!plan)
+ return;
+
+ /* Leave if no parameters */
+ if (!boundParams)
+ return;
+
+ /*
+ * Count the number of remote parameters available
+ * We need to take into account all the parameters
+ * that are prior to the latest available. This insures
+ * that remote node will not complain about an incorrect
+ * number of parameter. In case parameters with no types
+ * are taken into account, they are considered as NULL entries.
+ */
+ cntParam = 0;
+ for (i = 0; i < boundParams->numParams; i++)
+ {
+ if (OidIsValid(boundParams->params[i].ptype))
+ cntParam = i + 1;
+ }
+
+ /* If there are no parameters available, simply leave */
+ if (cntParam == 0)
+ return;
+
+ param_types = (Oid *) palloc(sizeof(Oid) * cntParam);
+
+ /* Then fill the array of types */
+ for (i = 0; i < cntParam; i++)
+ param_types[i] = boundParams->params[i].ptype;
+
+ /* Finally save the parameters in plan */
+ SetRemoteStatementName(plan->planTree, NULL,
+ cntParam, param_types, 0);
+
+ return;
+}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index cacc10b75d..b9380ac987 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -41,7 +41,7 @@
#ifdef PGXC
#include "commands/prepare.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 83e4033b16..81243dbc50 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -26,7 +26,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#ifdef PGXC
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e9c0b33a94..aa42732bd6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -54,7 +54,7 @@
#include "pgxc/pgxcnode.h"
#include "access/gtm.h"
#include "utils/lsyscache.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "tcop/tcopprot.h"
#include "nodes/nodes.h"
#include "pgxc/poolmgr.h"
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 95f51c52e3..27e0a86e4b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -55,7 +55,7 @@
#ifdef PGXC
#include "pgxc/locator.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "pgxc/execRemote.h"
#endif
#include "parser/parser.h"
diff --git a/src/backend/pgxc/Makefile b/src/backend/pgxc/Makefile
index 49a299b7c2..1fe1c12d02 100644
--- a/src/backend/pgxc/Makefile
+++ b/src/backend/pgxc/Makefile
@@ -11,6 +11,6 @@ subdir = src/backend/pgxc
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-SUBDIRS = locator plan pool barrier nodemgr copy xc_maintenance_mode
+SUBDIRS = locator pool barrier nodemgr copy xc_maintenance_mode
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/pgxc/plan/Makefile b/src/backend/pgxc/plan/Makefile
deleted file mode 100644
index c322c03656..0000000000
--- a/src/backend/pgxc/plan/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-#-------------------------------------------------------------------------
-#
-# Makefile--
-# Makefile for rewrite
-#
-# Portions Copyright(C) 2010-2012 Postgres-XC Development Group
-#
-# IDENTIFICATION
-# $PostgreSQL$
-#
-#-------------------------------------------------------------------------
-
-subdir = src/backend/pgxc/plan
-top_builddir = ../../../..
-include $(top_builddir)/src/Makefile.global
-
-OBJS = planner.o
-
-include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/pgxc/plan/planner.c b/src/backend/pgxc/plan/planner.c
deleted file mode 100644
index 2d6f968603..0000000000
--- a/src/backend/pgxc/plan/planner.c
+++ /dev/null
@@ -1,884 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * planner.c
- * Functions for generating a Postgres-XC plan.
- *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
- * Portions Copyright (c) 2010-2012 Postgres-XC Development Group
- *
- *
- * IDENTIFICATION
- * src/backend/pgxc/plan/planner.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-#include "miscadmin.h"
-#include "access/transam.h"
-#include "catalog/pg_aggregate.h"
-#include "catalog/pg_class.h"
-#include "catalog/pg_inherits_fn.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "catalog/pgxc_node.h"
-#include "commands/prepare.h"
-#include "executor/executor.h"
-#include "lib/stringinfo.h"
-#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
-#include "nodes/nodes.h"
-#include "nodes/parsenodes.h"
-#include "optimizer/clauses.h"
-#include "optimizer/pgxcship.h"
-#include "optimizer/planmain.h"
-#include "optimizer/planner.h"
-#include "optimizer/tlist.h"
-#include "parser/parse_agg.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_func.h"
-#include "parser/parse_relation.h"
-#include "parser/parsetree.h"
-#include "parser/parse_oper.h"
-#include "parser/parse_type.h"
-#include "pgxc/execRemote.h"
-#include "pgxc/pgxc.h"
-#include "pgxc/locator.h"
-#include "pgxc/nodemgr.h"
-#include "pgxc/planner.h"
-#include "tcop/pquery.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
-#include "utils/portal.h"
-#include "utils/syscache.h"
-#include "utils/numeric.h"
-#include "utils/memutils.h"
-#include "access/hash.h"
-#include "commands/tablecmds.h"
-#include "utils/timestamp.h"
-#include "utils/date.h"
-
-/* fast query shipping is enabled by default */
-bool enable_fast_query_shipping = true;
-
-static RemoteQuery *makeRemoteQuery(void);
-static void validate_part_col_updatable(const Query *query);
-static bool contains_temp_tables(List *rtable);
-static void pgxc_handle_unsupported_stmts(Query *query);
-static PlannedStmt *pgxc_FQS_planner(Query *query, int cursorOptions,
- ParamListInfo boundParams);
-static PlannedStmt *pgxc_handle_exec_direct(Query *query, int cursorOptions,
- ParamListInfo boundParams);
-static RemoteQuery *pgxc_FQS_create_remote_plan(Query *query,
- ExecNodes *exec_nodes,
- bool is_exec_direct);
-static void pgxc_set_remote_parameters(PlannedStmt *plan, ParamListInfo boundParams);
-
-
-/*
- * make_ctid_col_ref
- *
- * creates a Var for a column referring to ctid
- */
-
-static Var *
-make_ctid_col_ref(Query *qry)
-{
- ListCell *lc1, *lc2;
- RangeTblEntry *rte1, *rte2;
- int tableRTEs, firstTableRTENumber;
- RangeTblEntry *rte_in_query = NULL;
- AttrNumber attnum;
- Oid vartypeid;
- int32 type_mod;
- Oid varcollid;
-
- /*
- * If the query has more than 1 table RTEs where both are different, we can not add ctid to the query target list
- * We should in this case skip adding it to the target list and a WHERE CURRENT OF should then
- * fail saying the query is not a simply update able scan of table
- */
-
- tableRTEs = 0;
- foreach(lc1, qry->rtable)
- {
- rte1 = (RangeTblEntry *) lfirst(lc1);
-
- if (rte1->rtekind == RTE_RELATION)
- {
- tableRTEs++;
- if (tableRTEs > 1)
- {
- /*
- * See if we get two RTEs in case we have two references
- * to the same table with different aliases
- */
- foreach(lc2, qry->rtable)
- {
- rte2 = (RangeTblEntry *) lfirst(lc2);
-
- if (rte2->rtekind == RTE_RELATION)
- {
- if (rte2->relid != rte1->relid)
- {
- return NULL;
- }
- }
- }
- continue;
- }
- rte_in_query = rte1;
- }
- }
-
- if (tableRTEs > 1)
- {
- firstTableRTENumber = 0;
- foreach(lc1, qry->rtable)
- {
- rte1 = (RangeTblEntry *) lfirst(lc1);
- firstTableRTENumber++;
- if (rte1->rtekind == RTE_RELATION)
- {
- break;
- }
- }
- }
- else
- {
- firstTableRTENumber = 1;
- }
-
- attnum = specialAttNum("ctid");
- Assert(rte_in_query);
- get_rte_attribute_type(rte_in_query, attnum, &vartypeid, &type_mod, &varcollid);
- return makeVar(firstTableRTENumber, attnum, vartypeid, type_mod, varcollid, 0);
-}
-
-
-/*
- * Returns true if at least one temporary table is in use
- * in query (and its subqueries)
- */
-static bool
-contains_temp_tables(List *rtable)
-{
- ListCell *item;
-
- foreach(item, rtable)
- {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(item);
-
- if (rte->rtekind == RTE_RELATION)
- {
- if (IsTempTable(rte->relid))
- return true;
- }
- else if (rte->rtekind == RTE_SUBQUERY &&
- contains_temp_tables(rte->subquery->rtable))
- return true;
- }
-
- return false;
-}
-
-/*
- * Create an instance of RemoteQuery and initialize fields
- */
-static RemoteQuery *
-makeRemoteQuery(void)
-{
- RemoteQuery *result = makeNode(RemoteQuery);
- result->combine_type = COMBINE_TYPE_NONE;
- result->exec_type = EXEC_ON_DATANODES;
- result->exec_direct_type = EXEC_DIRECT_NONE;
-
- return result;
-}
-
-/*
- * get_plan_combine_type - determine combine type
- *
- * COMBINE_TYPE_SAME - for replicated updates
- * COMBINE_TYPE_SUM - for hash and round robin updates
- * COMBINE_TYPE_NONE - for operations where row_count is not applicable
- *
- * return NULL if it is not safe to be done in a single step.
- */
-static CombineType
-get_plan_combine_type(Query *query, char baselocatortype)
-{
-
- switch (query->commandType)
- {
- case CMD_INSERT:
- case CMD_UPDATE:
- case CMD_DELETE:
- return baselocatortype == LOCATOR_TYPE_REPLICATED ?
- COMBINE_TYPE_SAME : COMBINE_TYPE_SUM;
-
- default:
- return COMBINE_TYPE_NONE;
- }
- /* quiet compiler warning */
- return COMBINE_TYPE_NONE;
-}
-
-/*
- * get oid of the function whose name is passed as argument
- */
-
-static Oid
-get_fn_oid(char *fn_name, Oid *p_rettype)
-{
- Value *fn_nm;
- List *fn_name_list;
- FuncDetailCode fdc;
- bool retset;
- int nvargs;
- Oid *true_typeids;
- Oid func_oid;
-
- fn_nm = makeString(fn_name);
- fn_name_list = list_make1(fn_nm);
-
- fdc = func_get_detail(fn_name_list,
- NULL, /* argument expressions */
- NULL, /* argument names */
- 0, /* argument numbers */
- NULL, /* argument types */
- false, /* expand variable number or args */
- false, /* expand defaults */
- &func_oid, /* oid of the function - returned detail*/
- p_rettype, /* function return type - returned detail */
- &retset, /* - returned detail*/
- &nvargs, /* - returned detail*/
- &true_typeids, /* - returned detail */
- NULL /* arguemnt defaults returned*/
- );
-
- pfree(fn_name_list);
- if (fdc == FUNCDETAIL_NORMAL)
- {
- return func_oid;
- }
- return InvalidOid;
-}
-
-/*
- * Append ctid to the field list of step queries to support update
- * WHERE CURRENT OF. The ctid is not sent down to client but used as a key
- * to find target tuple.
- * PGXCTODO: Bug
- * This function modifies the original query to add ctid
- * and nodename in the targetlist. It should rather modify the targetlist of the
- * query to be shipped by the RemoteQuery node.
- */
-static void
-fetch_ctid_of(Plan *subtree, Query *query)
-{
- /* recursively process subnodes */
- if (innerPlan(subtree))
- fetch_ctid_of(innerPlan(subtree), query);
- if (outerPlan(subtree))
- fetch_ctid_of(outerPlan(subtree), query);
-
- /* we are only interested in RemoteQueries */
- if (IsA(subtree, RemoteQuery))
- {
- RemoteQuery *step = (RemoteQuery *) subtree;
- TargetEntry *te1;
- Query *temp_qry;
- FuncExpr *func_expr;
- AttrNumber resno;
- Oid funcid;
- Oid rettype;
- Var *ctid_expr;
- MemoryContext oldcontext;
- MemoryContext tmpcontext;
-
- tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
- "Temp Context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldcontext = MemoryContextSwitchTo(tmpcontext);
-
- /* Copy the query tree to make changes to the target list */
- temp_qry = copyObject(query);
- /* Get the number of entries in the target list */
- resno = list_length(temp_qry->targetList);
-
- /* Make a ctid column ref expr to add in target list */
- ctid_expr = make_ctid_col_ref(temp_qry);
- if (ctid_expr == NULL)
- {
- MemoryContextSwitchTo(oldcontext);
- MemoryContextDelete(tmpcontext);
- return;
- }
-
- te1 = makeTargetEntry((Expr *)ctid_expr, resno+1, NULL, false);
-
- /* add the target entry to the query target list */
- temp_qry->targetList = lappend(temp_qry->targetList, te1);
-
- /* PGXCTODO We can take this call in initialization rather than getting it always */
-
- /* Get the Oid of the function */
- funcid = get_fn_oid("pgxc_node_str", &rettype);
- if (OidIsValid(funcid))
- {
- StringInfoData deparsed_qry;
- TargetEntry *te2;
-
- /* create a function expression */
- func_expr = makeFuncExpr(funcid, rettype, NULL, InvalidOid, InvalidOid, COERCE_DONTCARE);
- /* make a target entry for function call */
- te2 = makeTargetEntry((Expr *)func_expr, resno+2, NULL, false);
- /* add the target entry to the query target list */
- temp_qry->targetList = lappend(temp_qry->targetList, te2);
-
- initStringInfo(&deparsed_qry);
- deparse_query(temp_qry, &deparsed_qry, NIL);
-
- MemoryContextSwitchTo(oldcontext);
-
- if (step->sql_statement != NULL)
- pfree(step->sql_statement);
-
- step->sql_statement = pstrdup(deparsed_qry.data);
-
- MemoryContextDelete(tmpcontext);
- }
- else
- {
- MemoryContextSwitchTo(oldcontext);
- MemoryContextDelete(tmpcontext);
- }
- }
-}
-
-/*
- * Build up a QueryPlan to execute on.
- *
- * This functions tries to find out whether
- * 1. The statement can be shipped to the Datanode and Coordinator is needed
- * only as a proxy - in which case, it creates a single node plan.
- * 2. The statement can be evaluated on the Coordinator completely - thus no
- * query shipping is involved and standard_planner() is invoked to plan the
- * statement
- * 3. The statement needs Coordinator as well as Datanode for evaluation -
- * again we use standard_planner() to plan the statement.
- *
- * The plan generated in either of the above cases is returned.
- */
-PlannedStmt *
-pgxc_planner(Query *query, int cursorOptions, ParamListInfo boundParams)
-{
- PlannedStmt *result;
-
- /* handle the un-supported statements, obvious errors etc. */
- pgxc_handle_unsupported_stmts(query);
-
- result = pgxc_handle_exec_direct(query, cursorOptions, boundParams);
- if (result)
- return result;
-
- /* see if can ship the query completely */
- result = pgxc_FQS_planner(query, cursorOptions, boundParams);
- if (result)
- return result;
-
- /* we need Coordinator for evaluation, invoke standard planner */
- result = standard_planner(query, cursorOptions, boundParams);
- pgxc_set_remote_parameters(result, boundParams);
- return result;
-}
-
-static PlannedStmt *
-pgxc_handle_exec_direct(Query *query, int cursorOptions,
- ParamListInfo boundParams)
-{
- PlannedStmt *result = NULL;
- PlannerGlobal *glob;
- PlannerInfo *root;
- /*
- * if the query has its utility set, it could be an EXEC_DIRECT statement,
- * check if it needs to be executed on Coordinator
- */
- if (query->utilityStmt &&
- IsA(query->utilityStmt, RemoteQuery))
- {
- RemoteQuery *node = (RemoteQuery *)query->utilityStmt;
- /* EXECUTE DIRECT statements on remote nodes don't need Coordinator */
- if (node->exec_direct_type != EXEC_DIRECT_NONE &&
- node->exec_direct_type != EXEC_DIRECT_LOCAL &&
- node->exec_direct_type != EXEC_DIRECT_LOCAL_UTILITY)
- {
- glob = makeNode(PlannerGlobal);
- glob->boundParams = boundParams;
- /* Create a PlannerInfo data structure, usually it is done for a subquery */
- root = makeNode(PlannerInfo);
- root->parse = query;
- root->glob = glob;
- root->query_level = 1;
- root->planner_cxt = CurrentMemoryContext;
- /* build the PlannedStmt result */
- result = makeNode(PlannedStmt);
- /* Try and set what we can, rest must have been zeroed out by makeNode() */
- result->commandType = query->commandType;
- result->canSetTag = query->canSetTag;
- /* Set result relations */
- if (query->commandType != CMD_SELECT)
- result->resultRelations = list_make1_int(query->resultRelation);
-
- result->planTree = (Plan *)pgxc_FQS_create_remote_plan(query, NULL, true);
- result->rtable = query->rtable;
- /*
- * We need to save plan dependencies, so that dropping objects will
- * invalidate the cached plan if it depends on those objects. Table
- * dependencies are available in glob->relationOids and all other
- * dependencies are in glob->invalItems. These fields can be retrieved
- * through set_plan_references().
- */
- result->planTree = set_plan_references(root, result->planTree);
- result->relationOids = glob->relationOids;
- result->invalItems = glob->invalItems;
- }
- }
-
- /* Set existing remote parameters */
- pgxc_set_remote_parameters(result, boundParams);
-
- return result;
-}
-/*
- * pgxc_handle_unsupported_stmts
- * Throw error for the statements that can not be handled in XC
- */
-static void
-pgxc_handle_unsupported_stmts(Query *query)
-{
- /*
- * PGXCTODO: This validation will not be removed
- * until we support moving tuples from one node to another
- * when the partition column of a table is updated
- */
- if (query->commandType == CMD_UPDATE)
- validate_part_col_updatable(query);
-
- if (query->returningList)
- ereport(ERROR,
- (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
- (errmsg("RETURNING clause not yet supported"))));
-}
-
-/*
- * pgxc_FQS_planner
- * The routine tries to see if the statement can be completely evaluated on the
- * Datanodes. In such cases Coordinator is not needed to evaluate the statement,
- * and just acts as a proxy. A statement can be completely shipped to the remote
- * node if every row of the result can be evaluated on a single Datanode.
- * For example:
- *
- * 1. SELECT * FROM tab1; where tab1 is a distributed table - Every row of the
- * result set can be evaluated at a single Datanode. Hence this statement is
- * completely shippable even though many Datanodes are involved in evaluating
- * complete result set. In such case Coordinator will be able to gather rows
- * arisign from individual Datanodes and proxy the result to the client.
- *
- * 2. SELECT count(*) FROM tab1; where tab1 is a distributed table - there is
- * only one row in the result but it needs input from all the Datanodes. Hence
- * this is not completely shippable.
- *
- * 3. SELECT count(*) FROM tab1; where tab1 is replicated table - since result
- * can be obtained from a single Datanode, this is a completely shippable
- * statement.
- *
- * fqs in the name of function is acronym for fast query shipping.
- */
-static PlannedStmt *
-pgxc_FQS_planner(Query *query, int cursorOptions, ParamListInfo boundParams)
-{
- PlannedStmt *result;
- PlannerGlobal *glob;
- PlannerInfo *root;
- ExecNodes *exec_nodes;
- Plan *top_plan;
-
- /* Try by-passing standard planner, if fast query shipping is enabled */
- if (!enable_fast_query_shipping)
- return NULL;
-
- /* Cursor options may come from caller or from DECLARE CURSOR stmt */
- if (query->utilityStmt &&
- IsA(query->utilityStmt, DeclareCursorStmt))
- cursorOptions |= ((DeclareCursorStmt *) query->utilityStmt)->options;
- /*
- * If the query can not be or need not be shipped to the Datanodes, don't
- * create any plan here. standard_planner() will take care of it.
- */
- exec_nodes = pgxc_is_query_shippable(query, 0);
- if (exec_nodes == NULL)
- return NULL;
-
- glob = makeNode(PlannerGlobal);
- glob->boundParams = boundParams;
- /* Create a PlannerInfo data structure, usually it is done for a subquery */
- root = makeNode(PlannerInfo);
- root->parse = query;
- root->glob = glob;
- root->query_level = 1;
- root->planner_cxt = CurrentMemoryContext;
-
- /*
- * We decided to ship the query to the Datanode/s, create a RemoteQuery node
- * for the same.
- */
- top_plan = (Plan *)pgxc_FQS_create_remote_plan(query, exec_nodes, false);
- /*
- * If creating a plan for a scrollable cursor, make sure it can run
- * backwards on demand. Add a Material node at the top at need.
- */
- if (cursorOptions & CURSOR_OPT_SCROLL)
- {
- if (!ExecSupportsBackwardScan(top_plan))
- top_plan = materialize_finished_plan(top_plan);
- }
-
- /*
- * Just before creating the PlannedStmt, do some final cleanup
- * We need to save plan dependencies, so that dropping objects will
- * invalidate the cached plan if it depends on those objects. Table
- * dependencies are available in glob->relationOids and all other
- * dependencies are in glob->invalItems. These fields can be retrieved
- * through set_plan_references().
- */
- top_plan = set_plan_references(root, top_plan);
-
- /* build the PlannedStmt result */
- result = makeNode(PlannedStmt);
- /* Try and set what we can, rest must have been zeroed out by makeNode() */
- result->commandType = query->commandType;
- result->canSetTag = query->canSetTag;
- result->utilityStmt = query->utilityStmt;
-
- /* Set result relations */
- if (query->commandType != CMD_SELECT)
- result->resultRelations = list_make1_int(query->resultRelation);
- result->planTree = top_plan;
- result->rtable = query->rtable;
- result->relationOids = glob->relationOids;
- result->invalItems = glob->invalItems;
-
- /*
- * If query is DECLARE CURSOR fetch CTIDs and node names from the remote node
- * Use CTID as a key to update/delete tuples on remote nodes when handling
- * WHERE CURRENT OF.
- */
- if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
- fetch_ctid_of(result->planTree, query);
-
- /* Set existing remote parameters */
- pgxc_set_remote_parameters(result, boundParams);
-
- return result;
-}
-
-static RemoteQuery *
-pgxc_FQS_create_remote_plan(Query *query, ExecNodes *exec_nodes, bool is_exec_direct)
-{
- RemoteQuery *query_step;
- StringInfoData buf;
- RangeTblEntry *dummy_rte;
-
- /* EXECUTE DIRECT statements have their RemoteQuery node already built when analyzing */
- if (is_exec_direct)
- {
- Assert(IsA(query->utilityStmt, RemoteQuery));
- query_step = (RemoteQuery *)query->utilityStmt;
- query->utilityStmt = NULL;
- }
- else
- {
- query_step = makeRemoteQuery();
- query_step->exec_nodes = exec_nodes;
- }
-
- Assert(query_step->exec_nodes);
-
- /* Datanodes should finalise the results of this query */
- query->qry_finalise_aggs = true;
-
- /* Deparse query tree to get step query. */
- if (query_step->sql_statement == NULL)
- {
- initStringInfo(&buf);
- deparse_query(query, &buf, NIL);
- query_step->sql_statement = pstrdup(buf.data);
- pfree(buf.data);
- }
- /*
- * PGXCTODO: we may route this same Query structure through
- * standard_planner, where we don't want Datanodes to finalise the results.
- * Turn it off. At some point, we will avoid routing the same query
- * structure through the standard_planner by modifying it only when it's not
- * be routed through standard_planner.
- */
- query->qry_finalise_aggs = false;
- /* Optimize multi-node handling */
- query_step->read_only = (query->commandType == CMD_SELECT && !query->hasForUpdate);
- query_step->has_row_marks = query->hasForUpdate;
-
- /* Check if temporary tables are in use in query */
- /* PGXC_FQS_TODO: scanning the rtable again for the queries should not be
- * needed. We should be able to find out if the query has a temporary object
- * while finding nodes for the objects. But there is no way we can convey
- * that information here. Till such a connection is available, this is it.
- */
- if (contains_temp_tables(query->rtable))
- query_step->is_temp = true;
-
- /*
- * We need to evaluate some expressions like the ExecNodes->en_expr at
- * Coordinator, prepare those for evaluation. Ideally we should call
- * preprocess_expression, but it needs PlannerInfo structure for the same
- */
- fix_opfuncids((Node *)(query_step->exec_nodes->en_expr));
- /*
- * PGXCTODO
- * When Postgres runs insert into t (a) values (1); against table
- * defined as create table t (a int, b int); the plan is looking
- * like insert into t (a,b) values (1,null);
- * Later executor is verifying plan, to make sure table has not
- * been altered since plan has been created and comparing table
- * definition with plan target list and output error if they do
- * not match.
- * I could not find better way to generate targetList for pgxc plan
- * then call standard planner and take targetList from the plan
- * generated by Postgres.
- */
- query_step->combine_type = get_plan_combine_type(
- query, query_step->exec_nodes->baselocatortype);
-
- /*
- * Create a dummy RTE for the remote query being created. Append the dummy
- * range table entry to the range table. Note that this modifies the master
- * copy the caller passed us, otherwise e.g EXPLAIN VERBOSE will fail to
- * find the rte the Vars built below refer to. Also create the tuple
- * descriptor for the result of this query from the base_tlist (targetlist
- * we used to generate the remote node query).
- */
- dummy_rte = makeNode(RangeTblEntry);
- dummy_rte->rtekind = RTE_REMOTE_DUMMY;
- /* Use a dummy relname... */
- if (is_exec_direct)
- dummy_rte->relname = "__EXECUTE_DIRECT__";
- else
- dummy_rte->relname = "__REMOTE_FQS_QUERY__";
- dummy_rte->eref = makeAlias("__REMOTE_FQS_QUERY__", NIL);
- /* Rest will be zeroed out in makeNode() */
-
- query->rtable = lappend(query->rtable, dummy_rte);
- query_step->scan.scanrelid = list_length(query->rtable);
- query_step->scan.plan.targetlist = query->targetList;
- query_step->base_tlist = query->targetList;
-
- return query_step;
-}
-
-
-/*
- * validate whether partition column of a table is being updated
- */
-static void
-validate_part_col_updatable(const Query *query)
-{
- RangeTblEntry *rte;
- RelationLocInfo *rel_loc_info;
- ListCell *lc;
-
- /* Make sure there is one table at least */
- if (query->rtable == NULL)
- return;
-
- rte = (RangeTblEntry *) list_nth(query->rtable, query->resultRelation - 1);
-
-
- if (rte != NULL && rte->relkind != RELKIND_RELATION)
- /* Bad relation type */
- return;
-
- /* See if we have the partitioned case. */
- rel_loc_info = GetRelationLocInfo(rte->relid);
-
- /* Any column updation on local relations is fine */
- if (!rel_loc_info)
- return;
-
-
- /* Only LOCATOR_TYPE_HASH & LOCATOR_TYPE_MODULO should be checked */
- if ( (rel_loc_info->partAttrName != NULL) &&
- ( (rel_loc_info->locatorType == LOCATOR_TYPE_HASH) || (rel_loc_info->locatorType == LOCATOR_TYPE_MODULO) ) )
- {
- /* It is a partitioned table, check partition column in targetList */
- foreach(lc, query->targetList)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(lc);
-
- if (tle->resjunk)
- continue;
-
- /*
- * See if we have a constant expression comparing against the
- * designated partitioned column
- */
- if (strcmp(tle->resname, rel_loc_info->partAttrName) == 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
- (errmsg("Partition column can't be updated in current version"))));
- }
- }
-}
-
-/*
- * AddRemoteQueryNode
- *
- * Add a Remote Query node to launch on Datanodes.
- * This can only be done for a query a Top Level to avoid
- * duplicated queries on Datanodes.
- */
-List *
-AddRemoteQueryNode(List *stmts, const char *queryString, RemoteQueryExecType remoteExecType, bool is_temp)
-{
- List *result = stmts;
-
- /* If node is appplied on EXEC_ON_NONE, simply return the list unchanged */
- if (remoteExecType == EXEC_ON_NONE)
- return result;
-
- /* Only a remote Coordinator is allowed to send a query to backend nodes */
- if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
- {
- RemoteQuery *step = makeNode(RemoteQuery);
- step->combine_type = COMBINE_TYPE_SAME;
- step->sql_statement = (char *) queryString;
- step->exec_type = remoteExecType;
- step->is_temp = is_temp;
- result = lappend(result, step);
- }
-
- return result;
-}
-
-/*
- * pgxc_query_contains_temp_tables
- *
- * Check if there is any temporary object used in given list of queries.
- */
-bool
-pgxc_query_contains_temp_tables(List *queries)
-{
- ListCell *elt;
-
- foreach(elt, queries)
- {
- Query *query = (Query *) lfirst(elt);
-
- if (!query)
- continue;
-
- switch(query->commandType)
- {
- case CMD_SELECT:
- case CMD_UPDATE:
- case CMD_INSERT:
- case CMD_DELETE:
- if (contains_temp_tables(query->rtable))
- return true;
- default:
- break;
- }
- }
-
- return false;
-}
-
-/*
- * pgxc_query_contains_utility
- *
- * Check if there is any utility statement in given list of queries.
- */
-bool
-pgxc_query_contains_utility(List *queries)
-{
- ListCell *elt;
-
- foreach(elt, queries)
- {
- Query *query = (Query *) lfirst(elt);
-
- if (!query)
- continue;
-
- if (query->commandType == CMD_UTILITY)
- return true;
- }
-
- return false;
-}
-
-
-/*
- * pgxc_set_remote_parameters
- *
- * Set the list of remote parameters for remote plan
- */
-static void
-pgxc_set_remote_parameters(PlannedStmt *plan, ParamListInfo boundParams)
-{
- Oid *param_types;
- int cntParam, i;
-
- /* Leave if no plan */
- if (!plan)
- return;
-
- /* Leave if no parameters */
- if (!boundParams)
- return;
-
- /*
- * Count the number of remote parameters available
- * We need to take into account all the parameters
- * that are prior to the latest available. This insures
- * that remote node will not complain about an incorrect
- * number of parameter. In case parameters with no types
- * are taken into account, they are considered as NULL entries.
- */
- cntParam = 0;
- for (i = 0; i < boundParams->numParams; i++)
- {
- if (OidIsValid(boundParams->params[i].ptype))
- cntParam = i + 1;
- }
-
- /* If there are no parameters available, simply leave */
- if (cntParam == 0)
- return;
-
- param_types = (Oid *) palloc(sizeof(Oid) * cntParam);
-
- /* Then fill the array of types */
- for (i = 0; i < cntParam; i++)
- param_types[i] = boundParams->params[i].ptype;
-
- /* Finally save the parameters in plan */
- SetRemoteStatementName(plan->planTree, NULL,
- cntParam, param_types, 0);
-
- return;
-}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5fb1c569b5..f171d76030 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -89,7 +89,7 @@
/* PGXC_COORD */
#include "pgxc/execRemote.h"
#include "pgxc/barrier.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "nodes/nodes.h"
#include "pgxc/poolmgr.h"
#include "pgxc/pgxcnode.h"
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 3b72dd98e6..3524410025 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -22,7 +22,7 @@
#include "pg_trace.h"
#ifdef PGXC
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "pgxc/execRemote.h"
#include "access/relscan.h"
#endif
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5f54b6b31d..446f53ddb6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -67,7 +67,7 @@
#include "pgxc/execRemote.h"
#include "pgxc/locator.h"
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "pgxc/poolutils.h"
#include "nodes/nodes.h"
#include "pgxc/poolmgr.h"
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ca39597de0..b89570cce7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -56,7 +56,7 @@
#include "parser/parsetree.h"
#ifdef PGXC
#include "pgxc/pgxc.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#endif
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9fba429984..fceaa8a7fe 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -62,7 +62,7 @@
#include "optimizer/pgxcship.h"
#include "pgxc/execRemote.h"
#include "pgxc/locator.h"
-#include "pgxc/planner.h"
+#include "optimizer/pgxcplan.h"
#include "pgxc/poolmgr.h"
#include "pgxc/nodemgr.h"
#include "pgxc/xc_maintenance_mode.h"
diff --git a/src/include/pgxc/planner.h b/src/include/optimizer/pgxcplan.h
index 786a78e702..2d9620630c 100644
--- a/src/include/pgxc/planner.h
+++ b/src/include/optimizer/pgxcplan.h
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
- * planner.h
- * Externally declared locator functions
+ * pgxcplan.h
+ * Postgres-XC specific planner interfaces and structures.
*
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 2010-2012 Postgres-XC Development Group
*
- * src/include/pgxc/planner.h
+ * src/include/optimizer/pgxcplan.h
*
*-------------------------------------------------------------------------
*/
@@ -123,17 +123,10 @@ extern bool enable_fast_query_shipping;
extern PlannedStmt *pgxc_planner(Query *query, int cursorOptions,
ParamListInfo boundParams);
-extern bool IsHashDistributable(Oid col_type);
-
-extern ExecNodes *IsJoinReducible(RemoteQuery *innernode, RemoteQuery *outernode,
- Relids in_relids, Relids out_relids,
- Join *join, JoinPath *join_path, List *rtable);
-
extern List *AddRemoteQueryNode(List *stmts, const char *queryString,
RemoteQueryExecType remoteExecType, bool is_temp);
extern bool pgxc_query_contains_temp_tables(List *queries);
extern bool pgxc_query_contains_utility(List *queries);
extern void pgxc_rqplan_adjust_tlist(RemoteQuery *rqplan);
-extern void pgxc_rqplan_adjust_vars(RemoteQuery *rqplan, Node *node);
#endif /* PGXCPLANNER_H */
diff --git a/src/include/pgxc/execRemote.h b/src/include/pgxc/execRemote.h
index 5e3535e4c2..9e23359ade 100644
--- a/src/include/pgxc/execRemote.h
+++ b/src/include/pgxc/execRemote.h
@@ -18,11 +18,11 @@
#include "locator.h"
#include "nodes/nodes.h"
#include "pgxcnode.h"
-#include "planner.h"
#include "access/tupdesc.h"
#include "executor/tuptable.h"
#include "nodes/execnodes.h"
#include "nodes/pg_list.h"
+#include "optimizer/pgxcplan.h"
#include "tcop/dest.h"
#include "tcop/pquery.h"
#include "utils/snapshot.h"