[PATCH] Possible wrong result from inlining a STRICT SQL function
Hi,
While looking at ScalarArrayOpExpr strictness, I came across what looks
like a wrong-result case involving SQL-function inlining.
Here is a small example:
CREATE TEMP TABLE t (x int);
INSERT INTO t VALUES (NULL), (1);
CREATE FUNCTION strict_any(int) RETURNS bool
LANGUAGE SQL STRICT IMMUTABLE AS
$$ SELECT $1 = ANY ('{}'::int[]) $$;
SELECT x IS NULL AS null_input,
strict_any(x) IS NULL AS result_is_null
FROM t
ORDER BY x NULLS FIRST;
Since the function is declared STRICT, I would expect result_is_null to
be true for the NULL input. On current master it is false. EXPLAIN
shows that the function has been inlined as:
x = ANY ('{}'::int[])
For an empty array, NULL = ANY(array) is FALSE rather than NULL, so the
inlined expression seems to bypass the function's NULL-on-NULL-input
behavior. Empty-array ALL expressions appear to have the same issue,
returning TRUE for a NULL scalar input after inlining.
It looks like contain_nonstrict_functions() now relies on
check_functions_in_node() for ScalarArrayOpExpr. That verifies that the
operator function is strict, but it does not account for whether the
array can be empty. Before 2f153ddfdd3, this code used
is_strict_saop() with falseOK = false.
That commit was a mechanical refactor to reduce duplication by routing
the per-node checks through check_functions_in_node(). For the mutable,
volatile and parallel-hazard walkers the SAOP case is a pure operator-
property check, so the conversion was behavior-preserving. The nonstrict
case is different: is_strict_saop(expr, false) also depends on whether
the array can be empty, which a function-OID check cannot express, so the
special case looks like it was dropped inadvertently.
Would restoring that SAOP-specific check be the right approach?
This is a separate issue from, but in the same is_strict_saop() area as,
the nullability question I raised earlier in [1]/messages/by-id/CAJTYsWV3vqRJmST-gv1NsXEef-zOnjVJpYS910aBaiuMij4nFg@mail.gmail.com.
Regards,
Ayush
[1]: /messages/by-id/CAJTYsWV3vqRJmST-gv1NsXEef-zOnjVJpYS910aBaiuMij4nFg@mail.gmail.com
/messages/by-id/CAJTYsWV3vqRJmST-gv1NsXEef-zOnjVJpYS910aBaiuMij4nFg@mail.gmail.com
Attachments:
0001-Fix-SAOP-strictness-check-for-SQL-function-inlining.patchapplication/octet-stream; name=0001-Fix-SAOP-strictness-check-for-SQL-function-inlining.patchDownload+51-1
Ayush Tiwari <ayushtiwari.slg01@gmail.com> writes:
It looks like contain_nonstrict_functions() now relies on
check_functions_in_node() for ScalarArrayOpExpr. That verifies that the
operator function is strict, but it does not account for whether the
array can be empty. Before 2f153ddfdd3, this code used
is_strict_saop() with falseOK = false.
Ugh, yes, clearly brain fade on my part. I think your proposed
test cases are quite excessive, but the code patch seems sound.
Will get it committed.
regards, tom lane
Looking closer at this one: just reinserting the old code for
ScalarArrayOpExpr isn't great. Assuming the SAOP is okay,
we'll fall through and call check_functions_in_node, which will
check the operator's strictness a second time. That doesn't
result in any wrong answer, but it wastes a syscache lookup.
I fixed that by converting the if-test series into an else-if
chain, which arguably it should have been from the beginning.
(Maybe the compiler can see that the if-conditions are mutually
exclusive, but why make it deduce that?)
Pushed with that correction.
regards, tom lane