diff options
| author | Mason S | 2010-05-13 01:09:44 +0000 |
|---|---|---|
| committer | Pavan Deolasee | 2011-05-19 16:38:46 +0000 |
| commit | 458f2058e628fe721b3d5fd42f7038a1403d97cc (patch) | |
| tree | 7f72364db74239e99bfd32d4f420bd069303a726 | |
| parent | b95b8c84c1f2b5cf5f9c55c5b4cb3448771660e2 (diff) | |
This is the first of some planned changes to recognize more "Postgres-XC safe"
queries, before we implement more complex cross-node operations.
This focuses on native pg_catalog based views like pg_settings.
This is because it used by pgadmin. With the attached patch, pgadmin no
longer complains about such queries because they are now supported.
The PostgreSQL query rewriter appears to convert the view into a subquery.
I modified the XC planner to check for pg_catalog table usage in the
FROM clause (and subqueries thereof).
In addition, pgadmin was sending "SET client_encoding to 'UNICODE'".
It was being swallowed and proxied by Postgres-XC, but no response was sent
back because it did not recognize the 'S' message response. I have now
added support for that, too.
Note that we have planned a whole separate task planned for proper SET
handling. These commands will now be processed, but the user should not
assume that these will remain set, due to Postgres-XC pooler.
Again, we will address this in the future.
With this patch pgadmin runs much more smoothly when connected to Postgres-XC.
Users can view schema information and issue SQL statements. There is
still an error window that pops up when clicking on a table because it
issues a "SELECT count(*) FROM table", which is not yet supported in
Postgres-XC. This problem will disappear soon however, as a patch is
almost ready for basic aggregate support.
| -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); |
