diff options
| -rw-r--r-- | src/backend/pgxc/plan/planner.c | 154 | ||||
| -rw-r--r-- | src/backend/pgxc/pool/combiner.c | 18 | ||||
| -rw-r--r-- | src/backend/pgxc/pool/datanode.c | 1 | ||||
| -rw-r--r-- | src/backend/utils/cache/lsyscache.c | 29 | ||||
| -rw-r--r-- | src/include/pgxc/locator.h | 10 | ||||
| -rw-r--r-- | src/include/utils/lsyscache.h | 3 |
6 files changed, 184 insertions, 31 deletions
diff --git a/src/backend/pgxc/plan/planner.c b/src/backend/pgxc/plan/planner.c index 0720b0fc3d..078d1e42d4 100644 --- a/src/backend/pgxc/plan/planner.c +++ b/src/backend/pgxc/plan/planner.c @@ -16,6 +16,7 @@ #include "postgres.h" #include "access/transam.h" #include "catalog/pg_type.h" +#include "catalog/pg_namespace.h" #include "nodes/parsenodes.h" #include "pgxc/locator.h" #include "pgxc/planner.h" @@ -691,6 +692,32 @@ examine_conditions_fromlist(Special_Conditions * conditions, List *rtables, /* + * Returns whether or not the rtable (and its subqueries) + * only contain pg_catalog entries. + */ +static bool +contains_only_pg_catalog (List *rtable) +{ + ListCell *item; + + /* May be complicated. Before giving up, just check for pg_catalog usage */ + foreach(item, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(item); + + if (rte->rtekind == RTE_RELATION) + { + if (get_rel_namespace(rte->relid) != PG_CATALOG_NAMESPACE) + return false; + } else if (rte->rtekind == RTE_SUBQUERY && + !contains_only_pg_catalog (rte->subquery->rtable)) + return false; + } + return true; +} + + +/* * get_plan_nodes - determine the nodes to execute the command on. * * Examines the "special" query conditions in determining execution node list. @@ -709,6 +736,8 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) RelationLocInfo *rel_loc_info; Exec_Nodes *exec_nodes; Exec_Nodes *test_exec_nodes; + TableUsageType table_usage_type = TABLE_USAGE_TYPE_NO_TABLE; + TableUsageType current_usage_type = TABLE_USAGE_TYPE_NO_TABLE; exec_nodes = NULL; @@ -733,13 +762,84 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) if (!examine_conditions_fromlist(special_conditions, query->rtable, treenode)) { - /* if too complicated, just return NULL */ + /* May be complicated. Before giving up, just check for pg_catalog usage */ + if (contains_only_pg_catalog (query->rtable)) + { + /* just pg_catalog tables */ + exec_nodes = (Exec_Nodes *) palloc0(sizeof(Exec_Nodes)); + exec_nodes->tableusagetype = TABLE_USAGE_TYPE_PGCATALOG; + free_special_relations(special_conditions); + free_join_list(); + return exec_nodes; + } + + /* complicated */ free_special_relations(special_conditions); free_join_list(); return NULL; } } - else if (!IsA(treenode, RangeTblRef)) + else if (IsA(treenode, RangeTblRef)) + { + RangeTblRef *rtr = (RangeTblRef *) treenode; + + /* look at the entity */ + RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); + + if (rte->rtekind == RTE_SUBQUERY) + { + /* + * Recursively call for subqueries. + * Note this also works for views, which are rewritten as subqueries. + */ + Exec_Nodes *sub_nodes = get_plan_nodes(query_plan, rte->subquery, isRead); + if (sub_nodes) + current_usage_type = sub_nodes->tableusagetype; + else + { + /* could be complicated */ + free_special_relations(special_conditions); + free_join_list(); + return NULL; + } + } + else if (rte->rtekind == RTE_RELATION) + { + /* Look for pg_catalog tables */ + if (get_rel_namespace(rte->relid) == PG_CATALOG_NAMESPACE) + current_usage_type = TABLE_USAGE_TYPE_PGCATALOG; + else + current_usage_type = TABLE_USAGE_TYPE_USER_TABLE; + } + else if (rte->rtekind == RTE_FUNCTION) + { + /* See if it is a catalog function */ + FuncExpr *funcexpr = (FuncExpr *) rte->funcexpr; + if (get_func_namespace(funcexpr->funcid) == PG_CATALOG_NAMESPACE) + current_usage_type = TABLE_USAGE_TYPE_PGCATALOG; + else + current_usage_type = TABLE_USAGE_TYPE_USER_TABLE; + } + else + { + /* could be complicated */ + free_special_relations(special_conditions); + free_join_list(); + return NULL; + } + + /* See if we have pg_catalog mixed with other tables */ + if (table_usage_type == TABLE_USAGE_TYPE_NO_TABLE) + table_usage_type = current_usage_type; + else if (current_usage_type != table_usage_type) + { + /* mixed- too complicated for us for now */ + free_special_relations(special_conditions); + free_join_list(); + return NULL; + } + } + else { /* could be complicated */ free_special_relations(special_conditions); @@ -748,6 +848,13 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) } } + /* If we are just dealing with pg_catalog, just return */ + if (table_usage_type == TABLE_USAGE_TYPE_PGCATALOG) + { + exec_nodes = (Exec_Nodes *) palloc0(sizeof(Exec_Nodes)); + exec_nodes->tableusagetype = TABLE_USAGE_TYPE_PGCATALOG; + return exec_nodes; + } /* Examine the WHERE clause, too */ if (!examine_conditions(special_conditions, query->rtable, @@ -806,10 +913,11 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) rel_loc_info = GetRelationLocInfo(rte->relid); if (!rel_loc_info) - return false; + return NULL; } exec_nodes = GetRelationNodes(rel_loc_info, NULL, isRead); + exec_nodes->tableusagetype = table_usage_type; } } /* check for partitioned col comparison against a literal */ @@ -828,6 +936,7 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) test_exec_nodes = GetRelationNodes( lit_comp->rel_loc_info, &(lit_comp->constant), true); + test_exec_nodes->tableusagetype = table_usage_type; if (exec_nodes == NULL) exec_nodes = test_exec_nodes; else @@ -864,6 +973,7 @@ get_plan_nodes(Query_Plan * query_plan, Query * query, bool isRead) return false; exec_nodes = GetRelationNodes(rel_loc_info, NULL, isRead); + exec_nodes->tableusagetype = table_usage_type; } free_special_relations(special_conditions); free_join_list(); @@ -1031,7 +1141,6 @@ GetQueryPlan(Node *parsetree, const char *sql_statement, List *querytree_list) * See if it is a SELECT with no relations, like SELECT 1+1 or * SELECT nextval('fred'), and just use coord. */ - query = (Query *) linitial(querytree_list); if (query_step->exec_nodes == NULL && (query->jointree->fromlist == NULL || query->jointree->fromlist->length == 0)) @@ -1039,38 +1148,21 @@ GetQueryPlan(Node *parsetree, const char *sql_statement, List *querytree_list) query_plan->exec_loc_type = EXEC_ON_COORD; else { - query_plan->exec_loc_type = EXEC_ON_DATA_NODES; - - if (query_step->exec_nodes == NULL) + if (query_step->exec_nodes != NULL + && query_step->exec_nodes->tableusagetype == TABLE_USAGE_TYPE_PGCATALOG) { - bool is_pg_catalog = false; - - /* before giving up, see if we are dealing with pg_catalog */ - if (nodeTag(parsetree) == T_SelectStmt) - { - ListCell *lc; - - is_pg_catalog = true; - foreach(lc, query->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - /* hack so that pg_catalog queries can run */ - if (rte->relid >= FirstNormalObjectId) - { - is_pg_catalog = false; - break; - } - } - if (is_pg_catalog) - query_plan->exec_loc_type = EXEC_ON_COORD; - } + /* pg_catalog query, run on coordinator */ + query_plan->exec_loc_type = EXEC_ON_COORD; + } + else + { + query_plan->exec_loc_type = EXEC_ON_DATA_NODES; /* * If the nodelist is NULL, it is not safe for us to * execute */ - if (!is_pg_catalog && StrictStatementChecking) + if (!query_step->exec_nodes && StrictStatementChecking) ereport(ERROR, (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), (errmsg("Cannot safely execute statement in a single step.")))); @@ -1220,7 +1312,7 @@ GetQueryPlan(Node *parsetree, const char *sql_statement, List *querytree_list) * data node will do */ case T_ExplainStmt: - query_step->exec_nodes->nodelist = lappend_int(query_step->exec_nodes->nodelist, GetAnyDataNode()); + query_step->exec_nodes->nodelist = GetAnyDataNode(); query_plan->exec_loc_type = EXEC_ON_DATA_NODES; break; diff --git a/src/backend/pgxc/pool/combiner.c b/src/backend/pgxc/pool/combiner.c index 8493125491..f7c7f34552 100644 --- a/src/backend/pgxc/pool/combiner.c +++ b/src/backend/pgxc/pool/combiner.c @@ -254,6 +254,24 @@ CombineResponse(ResponseCombiner combiner, char msg_type, char *msg_body, size_t pq_putmessage(msg_type, msg_body, len); } break; + case 'S': /* ParameterStatus (SET command) */ + if (combiner->request_type == REQUEST_TYPE_NOT_DEFINED) + combiner->request_type = REQUEST_TYPE_QUERY; + if (combiner->request_type != REQUEST_TYPE_QUERY) + { + /* Inconsistent responses */ + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("Unexpected response from the data nodes"))); + } + /* Proxy last */ + if (++combiner->description_count == combiner->node_count) + { + if (combiner->dest == DestRemote + || combiner->dest == DestRemoteExecute) + pq_putmessage(msg_type, msg_body, len); + } + break; case 'G': /* CopyInResponse */ if (combiner->request_type == REQUEST_TYPE_NOT_DEFINED) combiner->request_type = REQUEST_TYPE_COPY_IN; diff --git a/src/backend/pgxc/pool/datanode.c b/src/backend/pgxc/pool/datanode.c index 04d096625b..91f253371b 100644 --- a/src/backend/pgxc/pool/datanode.c +++ b/src/backend/pgxc/pool/datanode.c @@ -703,6 +703,7 @@ handle_response(DataNodeHandle * conn, ResponseCombiner combiner) break; case 'T': /* RowDescription */ case 'D': /* DataRow */ + case 'S': /* ParameterStatus */ /* no need to parse, just move cursor */ conn->inCursor += msg_len; CombineResponse(combiner, msg_type, diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 8247516bd1..dd72a4bfee 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -1298,6 +1298,35 @@ get_func_name(Oid funcid) return NULL; } +#ifdef PGXC +/* A function like this is in 9.0 */ +/* + * get_func_namespace + * + * Returns the pg_namespace OID associated with a given function. + */ +Oid +get_func_namespace(Oid funcid) +{ + HeapTuple tp; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp); + Oid result; + + result = functup->pronamespace; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} +#endif + /* * get_func_rettype * Given procedure id, return the function's result type. diff --git a/src/include/pgxc/locator.h b/src/include/pgxc/locator.h index 335701c282..46a8f9ec58 100644 --- a/src/include/pgxc/locator.h +++ b/src/include/pgxc/locator.h @@ -42,6 +42,15 @@ typedef struct } RelationLocInfo; +/* track if tables use pg_catalog */ +typedef enum +{ + TABLE_USAGE_TYPE_NO_TABLE, + TABLE_USAGE_TYPE_PGCATALOG, + TABLE_USAGE_TYPE_USER_TABLE, + TABLE_USAGE_TYPE_MIXED +} TableUsageType; + /* * Nodes to execute on * primarynodelist is for replicated table writes, where to execute first. @@ -53,6 +62,7 @@ typedef struct List *primarynodelist; List *nodelist; char baselocatortype; + TableUsageType tableusagetype; /* track pg_catalog usage */ } Exec_Nodes; diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 4428b22d59..854b3eac9a 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -76,6 +76,9 @@ extern Oid get_negator(Oid opno); extern RegProcedure get_oprrest(Oid opno); extern RegProcedure get_oprjoin(Oid opno); extern char *get_func_name(Oid funcid); +#ifdef PGXC +extern Oid get_func_namespace(Oid funcid); +#endif extern Oid get_func_rettype(Oid funcid); extern int get_func_nargs(Oid funcid); extern Oid get_func_signature(Oid funcid, Oid **argtypes, int *nargs); |
