[PATCH] Possible wrong result from inlining a STRICT SQL function

Started by Ayush Tiwari26 days ago3 messageshackers
Jump to latest
#1Ayush Tiwari
ayushtiwari.slg01@gmail.com

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
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Ayush Tiwari (#1)
Re: [PATCH] Possible wrong result from inlining a STRICT SQL function

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

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#2)
Re: [PATCH] Possible wrong result from inlining a STRICT SQL function

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