Merge pull request #3690 from citusdata/fix/limit_non_const

Correctly handle non-constant LIMIT/OFFSET clauses
pull/3693/head
Philip Dubé 2020-04-09 20:25:27 +00:00 committed by GitHub
commit d99043fe0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 8 deletions

View File

@ -4320,19 +4320,30 @@ WorkerLimitCount(Node *limitCount, Node *limitOffset, OrderByLimitReference
Node *workerLimitNode = NULL; Node *workerLimitNode = NULL;
LimitPushdownable canPushDownLimit = LIMIT_CANNOT_PUSHDOWN; LimitPushdownable canPushDownLimit = LIMIT_CANNOT_PUSHDOWN;
/* no limit node to push down */
if (limitCount == NULL) if (limitCount == NULL)
{ {
/* no limit node to push down */
return NULL;
}
if (!IsA(limitCount, Const))
{
/*
* We only push down constant LIMIT clauses to make sure we get back
* the minimum number of rows.
*/
return NULL;
}
if (limitOffset != NULL && !IsA(limitOffset, Const))
{
/*
* If OFFSET is not a constant then we cannot calculate the LIMIT to
* push down.
*/
return NULL; return NULL;
} }
/*
* During subquery pushdown planning original query is used. In that case,
* certain expressions such as parameters are not evaluated and converted
* into Consts on the op node.
*/
Assert(IsA(limitCount, Const));
Assert(limitOffset == NULL || IsA(limitOffset, Const));
/* /*
* If window functions are computed on coordinator, we cannot push down LIMIT. * If window functions are computed on coordinator, we cannot push down LIMIT.

View File

@ -1024,6 +1024,17 @@ DeferErrorIfQueryNotSupported(Query *queryTree)
errorHint = filterHint; errorHint = filterHint;
} }
if (FindNodeCheck((Node *) queryTree->limitCount, IsNodeSubquery))
{
preconditionsSatisfied = false;
errorMessage = "subquery in LIMIT is not supported in multi-shard queries";
}
if (FindNodeCheck((Node *) queryTree->limitOffset, IsNodeSubquery))
{
preconditionsSatisfied = false;
errorMessage = "subquery in OFFSET is not supported in multi-shard queries";
}
/* finally check and error out if not satisfied */ /* finally check and error out if not satisfied */
if (!preconditionsSatisfied) if (!preconditionsSatisfied)

View File

@ -520,4 +520,41 @@ SELECT
(1 row) (1 row)
SET client_min_messages TO NOTICE; SET client_min_messages TO NOTICE;
-- non constants should not push down
CREATE OR REPLACE FUNCTION my_limit()
RETURNS INT AS $$
BEGIN
RETURN 5;
END; $$ language plpgsql VOLATILE;
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT my_limit();
l_orderkey
---------------------------------------------------------------------
1
1
1
1
1
(5 rows)
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT 10 OFFSET my_limit();
l_orderkey
---------------------------------------------------------------------
1
2
3
3
3
3
3
3
4
5
(10 rows)
DROP FUNCTION my_limit();
-- subqueries should error out
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT (SELECT 10);
ERROR: subquery in LIMIT is not supported in multi-shard queries
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT 10 OFFSET (SELECT 10);
ERROR: subquery in OFFSET is not supported in multi-shard queries
DROP TABLE lineitem_hash; DROP TABLE lineitem_hash;

View File

@ -223,4 +223,21 @@ SELECT
LIMIT 5; LIMIT 5;
SET client_min_messages TO NOTICE; SET client_min_messages TO NOTICE;
-- non constants should not push down
CREATE OR REPLACE FUNCTION my_limit()
RETURNS INT AS $$
BEGIN
RETURN 5;
END; $$ language plpgsql VOLATILE;
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT my_limit();
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT 10 OFFSET my_limit();
DROP FUNCTION my_limit();
-- subqueries should error out
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT (SELECT 10);
SELECT l_orderkey FROM lineitem_hash ORDER BY l_orderkey LIMIT 10 OFFSET (SELECT 10);
DROP TABLE lineitem_hash; DROP TABLE lineitem_hash;