Fixes a bug that causes some DML queries containing aggregates to fail

pull/3661/head
Marco Slot 2020-03-25 13:46:43 +01:00 committed by Philip Dubé
parent d3fdade2e8
commit a65ffee266
3 changed files with 70 additions and 6 deletions

View File

@ -27,7 +27,7 @@
/* private function declarations */ /* private function declarations */
static bool IsVarOrParamSublink(Node *node); static bool IsVariableExpression(Node *node);
static Expr * citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, static Expr * citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation, Oid result_collation,
MasterEvaluationContext *masterEvaluationContext); MasterEvaluationContext *masterEvaluationContext);
@ -120,9 +120,19 @@ PartiallyEvaluateExpression(Node *expression,
else if (ShouldEvaluateExpression((Expr *) expression) && else if (ShouldEvaluateExpression((Expr *) expression) &&
ShouldEvaluateFunctionWithMasterContext(masterEvaluationContext)) ShouldEvaluateFunctionWithMasterContext(masterEvaluationContext))
{ {
/* don't call citus_evaluate_expr on nodes it cannot handle */ if (FindNodeCheck(expression, IsVariableExpression))
if (FindNodeCheck(expression, IsVarOrParamSublink))
{ {
/*
* The expression contains a variable expression (e.g. a stable function,
* which has a column reference as its input). That means that we cannot
* evaluate the expression on the coordinator, since the result depends
* on the input.
*
* Skipping function evaluation for these expressions is safe in most
* cases, since the function will always be re-evaluated for every input
* value. An exception is function calls that call another stable function
* that should not be re-evaluated, such as now().
*/
return (Node *) expression_tree_mutator(expression, return (Node *) expression_tree_mutator(expression,
PartiallyEvaluateExpression, PartiallyEvaluateExpression,
masterEvaluationContext); masterEvaluationContext);
@ -211,11 +221,23 @@ ShouldEvaluateExpression(Expr *expression)
/* /*
* IsVarOrParamSublink returns whether node is a Var or PARAM_SUBLINK param. * IsVariableExpression returns whether the given node is a variable expression,
* meaning its result depends on the input data and is not constant for the whole
* query.
*/ */
static bool static bool
IsVarOrParamSublink(Node *node) IsVariableExpression(Node *node)
{ {
if (IsA(node, Aggref))
{
return true;
}
if (IsA(node, WindowFunc))
{
return true;
}
if (IsA(node, Param)) if (IsA(node, Param))
{ {
/* ExecInitExpr cannot handle PARAM_SUBLINK */ /* ExecInitExpr cannot handle PARAM_SUBLINK */

View File

@ -163,9 +163,31 @@ DELETE
FROM table_1 FROM table_1
WHERE key >= (SELECT min(KEY) FROM table_1) WHERE key >= (SELECT min(KEY) FROM table_1)
AND value > now() - interval '1 hour'; AND value > now() - interval '1 hour';
CREATE OR REPLACE FUNCTION stable_squared(int)
RETURNS int STABLE
LANGUAGE plpgsql
AS $function$
BEGIN
RAISE NOTICE 'stable_fn called';
RETURN $1 * $1;
END;
$function$;
SELECT create_distributed_function('stable_squared(int)');
create_distributed_function
---------------------------------------------------------------------
(1 row)
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared(count(*)::int) y FROM example GROUP BY key) a WHERE example.key = a.key;
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared((count(*) OVER ())::int) y FROM example GROUP BY key) a WHERE example.key = a.key;
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared(grouping(key)) y FROM example GROUP BY key) a WHERE example.key = a.key;
DROP SCHEMA multi_function_evaluation CASCADE; DROP SCHEMA multi_function_evaluation CASCADE;
NOTICE: drop cascades to 4 other objects NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table example DETAIL: drop cascades to table example
drop cascades to sequence example_value_seq drop cascades to sequence example_value_seq
drop cascades to function stable_fn() drop cascades to function stable_fn()
drop cascades to table table_1 drop cascades to table table_1
drop cascades to function stable_squared(integer)

View File

@ -145,4 +145,24 @@ FROM table_1
WHERE key >= (SELECT min(KEY) FROM table_1) WHERE key >= (SELECT min(KEY) FROM table_1)
AND value > now() - interval '1 hour'; AND value > now() - interval '1 hour';
CREATE OR REPLACE FUNCTION stable_squared(int)
RETURNS int STABLE
LANGUAGE plpgsql
AS $function$
BEGIN
RAISE NOTICE 'stable_fn called';
RETURN $1 * $1;
END;
$function$;
SELECT create_distributed_function('stable_squared(int)');
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared(count(*)::int) y FROM example GROUP BY key) a WHERE example.key = a.key;
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared((count(*) OVER ())::int) y FROM example GROUP BY key) a WHERE example.key = a.key;
UPDATE example SET value = timestamp '10-10-2000 00:00'
FROM (SELECT key, stable_squared(grouping(key)) y FROM example GROUP BY key) a WHERE example.key = a.key;
DROP SCHEMA multi_function_evaluation CASCADE; DROP SCHEMA multi_function_evaluation CASCADE;