From cb1dfd0a17646865c591b5035b23b21c08afaffb Mon Sep 17 00:00:00 2001 From: Murat Tuncer Date: Fri, 30 Dec 2016 12:45:49 +0300 Subject: [PATCH] Add hint to errored real time queries --- .../planner/multi_logical_planner.c | 105 +++++++++++++++--- .../regress/expected/multi_router_planner.out | 48 ++++---- .../regress/expected/multi_simple_queries.out | 12 +- .../expected/multi_verify_no_subquery.out | 15 +-- .../regress/output/multi_outer_join.source | 12 +- .../output/multi_outer_join_reference.source | 16 +-- src/test/regress/output/multi_subquery.source | 3 +- .../regress/output/multi_subquery_0.source | 3 +- 8 files changed, 139 insertions(+), 75 deletions(-) diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index e67a68b3f..9ec88a873 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -17,6 +17,7 @@ #include "access/nbtree.h" #include "catalog/pg_am.h" #include "commands/defrem.h" +#include "distributed/colocation_utils.h" #include "distributed/metadata_cache.h" #include "distributed/multi_logical_optimizer.h" #include "distributed/multi_logical_planner.h" @@ -57,6 +58,7 @@ static RuleApplyFunction RuleApplyFunctionArray[JOIN_RULE_LAST] = { 0 }; /* join static MultiNode * MultiPlanTree(Query *queryTree); static void ErrorIfQueryNotSupported(Query *queryTree); static bool HasUnsupportedJoinWalker(Node *node, void *context); +static bool ErrorHintRequired(const char *errorHint, Query *queryTree); static void ErrorIfSubqueryNotSupported(Query *subqueryTree); static bool HasTablesample(Query *queryTree); static bool HasOuterJoin(Query *queryTree); @@ -362,95 +364,118 @@ MultiPlanTree(Query *queryTree) static void ErrorIfQueryNotSupported(Query *queryTree) { - char *errorDetail = NULL; + char *errorMessage = NULL; bool hasTablesample = false; bool hasUnsupportedJoin = false; bool hasComplexJoinOrder = false; bool hasComplexRangeTableType = false; bool preconditionsSatisfied = true; + const char *errorHint = NULL; + const char *joinHint = "Consider joining tables on partition column and have " + "equal filter on joining columns."; + const char *filterHint = "Consider using an equality filter on the distributed " + "table's partition column."; if (queryTree->hasSubLinks) { preconditionsSatisfied = false; - errorDetail = "Subqueries other than in from-clause are currently unsupported"; + errorMessage = "could not run distributed query with subquery outside the " + "FROM clause"; + errorHint = filterHint; } if (queryTree->hasWindowFuncs) { preconditionsSatisfied = false; - errorDetail = "Window functions are currently unsupported"; + errorMessage = "could not run distributed query with window functions"; + errorHint = filterHint; } if (queryTree->setOperations) { preconditionsSatisfied = false; - errorDetail = "Union, Intersect, or Except are currently unsupported"; + errorMessage = "could not run distributed query with UNION, INTERSECT, or " + "EXCEPT"; + errorHint = filterHint; } if (queryTree->hasRecursive) { preconditionsSatisfied = false; - errorDetail = "Recursive queries are currently unsupported"; + errorMessage = "could not run distributed query with RECURSIVE"; + errorHint = filterHint; } if (queryTree->cteList) { preconditionsSatisfied = false; - errorDetail = "Common Table Expressions are currently unsupported"; + errorMessage = "could not run distributed query with common table expressions"; + errorHint = filterHint; } if (queryTree->hasForUpdate) { preconditionsSatisfied = false; - errorDetail = "For Update/Share commands are currently unsupported"; + errorMessage = "could not run distributed query with FOR UPDATE/SHARE commands"; + errorHint = filterHint; } if (queryTree->distinctClause) { preconditionsSatisfied = false; - errorDetail = "Distinct clause is currently unsupported"; + errorMessage = "could not run distributed query with DISTINCT clause"; + errorHint = filterHint; } if (queryTree->groupingSets) { preconditionsSatisfied = false; - errorDetail = "Grouping sets, cube, and rollup is currently unsupported"; + errorMessage = "could not run distributed query with GROUPING SETS, CUBE, " + "or ROLLUP"; + errorHint = filterHint; } hasTablesample = HasTablesample(queryTree); if (hasTablesample) { preconditionsSatisfied = false; - errorDetail = "Tablesample is currently unsupported"; + errorMessage = "could not run distributed query which use TABLESAMPLE"; + errorHint = filterHint; } hasUnsupportedJoin = HasUnsupportedJoinWalker((Node *) queryTree->jointree, NULL); if (hasUnsupportedJoin) { preconditionsSatisfied = false; - errorDetail = "Join types other than inner/outer joins are currently unsupported"; + errorMessage = "could not run distributed query with join types other than " + "INNER or OUTER JOINS"; + errorHint = joinHint; } hasComplexJoinOrder = HasComplexJoinOrder(queryTree); if (hasComplexJoinOrder) { preconditionsSatisfied = false; - errorDetail = "Complex join orders are currently unsupported"; + errorMessage = "could not run distributed query with complex join orders"; + errorHint = joinHint; } hasComplexRangeTableType = HasComplexRangeTableType(queryTree); if (hasComplexRangeTableType) { preconditionsSatisfied = false; - errorDetail = "Complex table expressions are currently unsupported"; + errorMessage = "could not run distributed query with complex table expressions"; + errorHint = filterHint; } + /* finally check and error out if not satisfied */ if (!preconditionsSatisfied) { + bool showHint = ErrorHintRequired(errorHint, queryTree); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning on this query"), - errdetail("%s", errorDetail))); + errmsg("%s", errorMessage), + showHint ? errhint("%s", errorHint) : 0)); } } @@ -514,6 +539,56 @@ HasUnsupportedJoinWalker(Node *node, void *context) } +/* + * ErrorHintRequired returns true if error hint shold be displayed with the + * query error message. Error hint is valid only for queries involving reference + * and hash partitioned tables. If more than one hash distributed table is + * present we display the hint only if the tables are colocated. If the query + * only has reference table(s), then it is handled by router planner. + */ +static bool +ErrorHintRequired(const char *errorHint, Query *queryTree) +{ + List *rangeTableList = NIL; + ListCell *rangeTableCell = NULL; + List *colocationIdList = NIL; + + if (errorHint == NULL) + { + return false; + } + + ExtractRangeTableRelationWalker((Node *) queryTree, &rangeTableList); + foreach(rangeTableCell, rangeTableList) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rangeTableCell); + Oid relationId = rte->relid; + char partitionMethod = PartitionMethod(relationId); + if (partitionMethod == DISTRIBUTE_BY_NONE) + { + continue; + } + else if (partitionMethod == DISTRIBUTE_BY_HASH) + { + int colocationId = TableColocationId(relationId); + colocationIdList = list_append_unique_int(colocationIdList, colocationId); + } + else + { + return false; + } + } + + /* do not display the hint if there are more than one colocation group */ + if (list_length(colocationIdList) > 1) + { + return false; + } + + return true; +} + + /* * ErrorIfSubqueryNotSupported checks that we can perform distributed planning for * the given subquery. diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index 3c47a4d36..46f9e34a3 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -334,8 +334,8 @@ id_title AS (SELECT id, title from articles_hash WHERE author_id = 2) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; DEBUG: predicate pruning for shardId 840001 DEBUG: predicate pruning for shardId 840000 -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- recursive CTEs are supported when filtered on partition column CREATE TABLE company_employees (company_id int, employee_id int, manager_id int); SELECT master_create_distributed_table('company_employees', 'company_id', 'hash'); @@ -416,8 +416,8 @@ SELECT * FROM hierarchy WHERE LEVEL <= 2; DEBUG: predicate pruning for shardId 840004 DEBUG: predicate pruning for shardId 840005 DEBUG: predicate pruning for shardId 840006 -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- logically wrong query, query involves different shards -- from the same table WITH RECURSIVE hierarchy as ( @@ -437,8 +437,8 @@ DEBUG: predicate pruning for shardId 840006 DEBUG: predicate pruning for shardId 840003 DEBUG: predicate pruning for shardId 840004 DEBUG: predicate pruning for shardId 840005 -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- CTE with queries other than SELECT is not supported WITH new_article AS ( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * @@ -504,8 +504,8 @@ SELECT FROM articles_hash WHERE author_id = 1 or author_id = 2 GROUP BY GROUPING SETS ((id),(subtitle)); -ERROR: cannot perform distributed planning on this query -DETAIL: Grouping sets, cube, and rollup is currently unsupported +ERROR: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP +HINT: Consider using an equality filter on the distributed table's partition column. -- queries which involve functions in FROM clause are supported if it goes to a single worker. SELECT * FROM articles_hash, position('om' in 'Thomas') WHERE author_id = 1; DEBUG: predicate pruning for shardId 840001 @@ -540,17 +540,17 @@ DEBUG: Plan is router executable -- they are not supported if multiple workers are involved SELECT * FROM articles_hash, position('om' in 'Thomas') WHERE author_id = 1 or author_id = 2; -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- subqueries are not supported in WHERE clause in Citus SELECT * FROM articles_hash WHERE author_id IN (SELECT id FROM authors_hash WHERE name LIKE '%a'); ERROR: cannot plan queries that include both regular and partitioned relations SELECT * FROM articles_hash WHERE author_id IN (SELECT author_id FROM articles_hash WHERE author_id = 1 or author_id = 3); -ERROR: cannot perform distributed planning on this query -DETAIL: Join types other than inner/outer joins are currently unsupported +ERROR: could not run distributed query with join types other than INNER or OUTER JOINS +HINT: Consider joining tables on partition column and have equal filter on joining columns. SELECT * FROM articles_hash WHERE author_id = (SELECT 1); -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries other than in from-clause are currently unsupported +ERROR: could not run distributed query with subquery outside the FROM clause +HINT: Consider using an equality filter on the distributed table's partition column. -- unless the query can be transformed into a join SELECT * FROM articles_hash WHERE author_id IN (SELECT author_id FROM articles_hash WHERE author_id = 2) @@ -641,8 +641,8 @@ HINT: Set citus.task_executor_type to "task-tracker". -- subqueries are not supported in SELECT clause SELECT a.title AS name, (SELECT a2.id FROM articles_single_shard_hash a2 WHERE a.id = a2.id LIMIT 1) AS special_price FROM articles_hash a; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries other than in from-clause are currently unsupported +ERROR: could not run distributed query with subquery outside the FROM clause +HINT: Consider using an equality filter on the distributed table's partition column. -- simple lookup query SELECT * FROM articles_hash @@ -748,8 +748,8 @@ SELECT a.author_id as first_author, b.word_count as second_word_count LIMIT 3; DEBUG: predicate pruning for shardId 840000 DEBUG: Found no worker with all shard placements -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- single shard select with limit is router plannable SELECT * FROM articles_hash @@ -961,8 +961,8 @@ SET client_min_messages to 'NOTICE'; (SELECT * FROM articles_hash WHERE author_id = 1) UNION (SELECT * FROM articles_hash WHERE author_id = 2); -ERROR: cannot perform distributed planning on this query -DETAIL: Union, Intersect, or Except are currently unsupported +ERROR: could not run distributed query with UNION, INTERSECT, or EXCEPT +HINT: Consider using an equality filter on the distributed table's partition column. SELECT * FROM ( (SELECT * FROM articles_hash WHERE author_id = 1) UNION @@ -1317,13 +1317,13 @@ DEBUG: Plan is router executable SELECT id, MIN(id) over (order by word_count) FROM articles_hash WHERE author_id = 1 or author_id = 2; -ERROR: cannot perform distributed planning on this query -DETAIL: Window functions are currently unsupported +ERROR: could not run distributed query with window functions +HINT: Consider using an equality filter on the distributed table's partition column. SELECT LAG(title, 1) over (ORDER BY word_count) prev, title, word_count FROM articles_hash WHERE author_id = 5 or author_id = 2; -ERROR: cannot perform distributed planning on this query -DETAIL: Window functions are currently unsupported +ERROR: could not run distributed query with window functions +HINT: Consider using an equality filter on the distributed table's partition column. -- where false queries are router plannable SELECT * FROM articles_hash diff --git a/src/test/regress/expected/multi_simple_queries.out b/src/test/regress/expected/multi_simple_queries.out index 7351d0983..0c650f34c 100644 --- a/src/test/regress/expected/multi_simple_queries.out +++ b/src/test/regress/expected/multi_simple_queries.out @@ -176,16 +176,16 @@ SELECT author_id, sum(word_count) AS corpus_size FROM articles -- UNION/INTERSECT queries are unsupported if on multiple shards SELECT * FROM articles WHERE author_id = 10 UNION SELECT * FROM articles WHERE author_id = 2; -ERROR: cannot perform distributed planning on this query -DETAIL: Union, Intersect, or Except are currently unsupported +ERROR: could not run distributed query with UNION, INTERSECT, or EXCEPT +HINT: Consider using an equality filter on the distributed table's partition column. -- queries using CTEs are unsupported WITH long_names AS ( SELECT id FROM authors WHERE char_length(name) > 15 ) SELECT title FROM articles; ERROR: cannot plan queries that include both regular and partitioned relations -- queries which involve functions in FROM clause are unsupported. SELECT * FROM articles, position('om' in 'Thomas'); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions +HINT: Consider using an equality filter on the distributed table's partition column. -- subqueries are not supported in WHERE clause in Citus SELECT * FROM articles WHERE author_id IN (SELECT id FROM authors WHERE name LIKE '%a'); ERROR: cannot plan queries that include both regular and partitioned relations @@ -250,8 +250,8 @@ ORDER BY articles.id; -- subqueries are not supported in SELECT clause SELECT a.title AS name, (SELECT a2.id FROM articles_single_shard a2 WHERE a.id = a2.id LIMIT 1) AS special_price FROM articles a; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries other than in from-clause are currently unsupported +ERROR: could not run distributed query with subquery outside the FROM clause +HINT: Consider using an equality filter on the distributed table's partition column. -- joins are not supported between local and distributed tables SELECT title, authors.name FROM authors, articles WHERE authors.id = articles.author_id; ERROR: cannot plan queries that include both regular and partitioned relations diff --git a/src/test/regress/expected/multi_verify_no_subquery.out b/src/test/regress/expected/multi_verify_no_subquery.out index 0995761cf..ae1459c90 100644 --- a/src/test/regress/expected/multi_verify_no_subquery.out +++ b/src/test/regress/expected/multi_verify_no_subquery.out @@ -6,18 +6,13 @@ ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1030000; SELECT * FROM lineitem WHERE l_orderkey IN (SELECT l_orderkey FROM lineitem WHERE l_quantity > 0); -ERROR: cannot perform distributed planning on this query -DETAIL: Join types other than inner/outer joins are currently unsupported +ERROR: could not run distributed query with join types other than INNER or OUTER JOINS SELECT l_quantity FROM lineitem WHERE EXISTS (SELECT 1 FROM orders WHERE o_orderkey = l_orderkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Join types other than inner/outer joins are currently unsupported +ERROR: could not run distributed query with join types other than INNER or OUTER JOINS SELECT l_quantity FROM lineitem WHERE l_orderkey IN (SELECT o_orderkey FROM orders); -ERROR: cannot perform distributed planning on this query -DETAIL: Join types other than inner/outer joins are currently unsupported +ERROR: could not run distributed query with join types other than INNER or OUTER JOINS SELECT l_orderkey FROM lineitem WHERE l_quantity > ALL(SELECT o_orderkey FROM orders); -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries other than in from-clause are currently unsupported +ERROR: could not run distributed query with subquery outside the FROM clause SELECT l_quantity FROM lineitem WHERE l_orderkey = (SELECT min(o_orderkey) FROM orders); -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries other than in from-clause are currently unsupported +ERROR: could not run distributed query with subquery outside the FROM clause diff --git a/src/test/regress/output/multi_outer_join.source b/src/test/regress/output/multi_outer_join.source index fa1296c80..a59b81243 100644 --- a/src/test/regress/output/multi_outer_join.source +++ b/src/test/regress/output/multi_outer_join.source @@ -329,8 +329,7 @@ FROM LEFT JOIN multi_outer_join_right r1 ON (l1.l_custkey = r1.r_custkey) LEFT JOIN multi_outer_join_right r2 ON (l1.l_custkey = r2.r_custkey) RIGHT JOIN multi_outer_join_left l2 ON (r2.r_custkey = l2.l_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders -- add an anti-join, this should also error out SELECT * @@ -341,8 +340,7 @@ FROM RIGHT JOIN multi_outer_join_left l2 ON (r2.r_custkey = l2.l_custkey) WHERE r1.r_custkey is NULL; -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders -- Three way join 2-2-1 (local + broadcast join) should work SELECT l_custkey, r_custkey, t_custkey @@ -387,8 +385,7 @@ FROM multi_outer_join_left l1 LEFT JOIN multi_outer_join_right r1 ON (l1.l_custkey = r1.r_custkey) RIGHT JOIN multi_outer_join_third t1 ON (r1.r_custkey = t1.t_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders -- Right join with single shard left most table should work SELECT t_custkey, r_custkey, l_custkey @@ -447,8 +444,7 @@ FROM multi_outer_join_third t1 RIGHT JOIN multi_outer_join_right r1 ON (t1.t_custkey = r1.r_custkey) RIGHT JOIN multi_outer_join_left l1 ON (r1.r_custkey = l1.l_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders -- full outer join should work with 1-1 matched shards SELECT l_custkey, r_custkey diff --git a/src/test/regress/output/multi_outer_join_reference.source b/src/test/regress/output/multi_outer_join_reference.source index 502d3cf2e..88fa461fd 100644 --- a/src/test/regress/output/multi_outer_join_reference.source +++ b/src/test/regress/output/multi_outer_join_reference.source @@ -358,8 +358,8 @@ FROM LEFT JOIN multi_outer_join_right_reference r1 ON (l1.l_custkey = r1.r_custkey) LEFT JOIN multi_outer_join_right_reference r2 ON (l1.l_custkey = r2.r_custkey) RIGHT JOIN multi_outer_join_left_hash l2 ON (r2.r_custkey = l2.l_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders +HINT: Consider joining tables on partition column and have equal filter on joining columns. -- add an anti-join, this should also error out SELECT * @@ -370,8 +370,8 @@ FROM RIGHT JOIN multi_outer_join_left_hash l2 ON (r2.r_custkey = l2.l_custkey) WHERE r1.r_custkey is NULL; -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders +HINT: Consider joining tables on partition column and have equal filter on joining columns. -- Three way join 2-1-1 (broadcast + broadcast join) should work SELECT l_custkey, r_custkey, t_custkey @@ -417,8 +417,8 @@ FROM multi_outer_join_left_hash l1 LEFT JOIN multi_outer_join_right_hash r1 ON (l1.l_custkey = r1.r_custkey) RIGHT JOIN multi_outer_join_third_reference t1 ON (r1.r_custkey = t1.t_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders +HINT: Consider joining tables on partition column and have equal filter on joining columns. -- Right join with single shard left most table should work SELECT t_custkey, r_custkey, l_custkey @@ -479,8 +479,8 @@ FROM multi_outer_join_third_reference t1 RIGHT JOIN multi_outer_join_right_hash r1 ON (t1.t_custkey = r1.r_custkey) RIGHT JOIN multi_outer_join_left_hash l1 ON (r1.r_custkey = l1.l_custkey); -ERROR: cannot perform distributed planning on this query -DETAIL: Complex join orders are currently unsupported +ERROR: could not run distributed query with complex join orders +HINT: Consider joining tables on partition column and have equal filter on joining columns. -- full outer join should work with 1-1 matched shards SELECT l_custkey, r_custkey diff --git a/src/test/regress/output/multi_subquery.source b/src/test/regress/output/multi_subquery.source index ec5ef9760..0b981a1fc 100644 --- a/src/test/regress/output/multi_subquery.source +++ b/src/test/regress/output/multi_subquery.source @@ -178,8 +178,7 @@ SELECT count(*) FROM (SELECT l_orderkey FROM lineitem_subquery) UNION ALL (SELECT 1::bigint) ) b; -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions -- Check that we error out if queries in union do not include partition columns. SELECT count(*) FROM ( diff --git a/src/test/regress/output/multi_subquery_0.source b/src/test/regress/output/multi_subquery_0.source index d00521980..3d01633a0 100644 --- a/src/test/regress/output/multi_subquery_0.source +++ b/src/test/regress/output/multi_subquery_0.source @@ -178,8 +178,7 @@ SELECT count(*) FROM (SELECT l_orderkey FROM lineitem_subquery) UNION ALL (SELECT 1::bigint) ) b; -ERROR: cannot perform distributed planning on this query -DETAIL: Complex table expressions are currently unsupported +ERROR: could not run distributed query with complex table expressions -- Check that we error out if queries in union do not include partition columns. SELECT count(*) FROM (