diff options
author | Ashutosh Bapat | 2012-10-11 06:58:02 +0000 |
---|---|---|
committer | Ashutosh Bapat | 2012-10-11 06:58:02 +0000 |
commit | 7b633f16bf11ade60acef4520c614dc3dd5704b8 (patch) | |
tree | fe5495c09b4b51f3a0471244623450257f795b1d | |
parent | 5ccf23fc829af560f4ddb325e0a6f5eacfdfd10c (diff) |
Move Postgres-XC specific planner code to appropriate PostgreSQL directories.
-rw-r--r-- | src/backend/catalog/pg_proc.c | 1 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 2 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 2 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/path/pgxcpath.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/plan/pgxcplan.c | 818 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 2 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 2 | ||||
-rw-r--r-- | src/backend/pgxc/Makefile | 2 | ||||
-rw-r--r-- | src/backend/pgxc/plan/Makefile | 19 | ||||
-rw-r--r-- | src/backend/pgxc/plan/planner.c | 884 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 2 | ||||
-rw-r--r-- | src/backend/tcop/pquery.c | 2 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 2 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 2 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 2 | ||||
-rw-r--r-- | src/include/optimizer/pgxcplan.h (renamed from src/include/pgxc/planner.h) | 13 | ||||
-rw-r--r-- | src/include/pgxc/execRemote.h | 2 |
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" |