mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Fix failure to validate the result of select_common_type().
Although select_common_type() has a failure-return convention, an apparent successful return just provides a type OID that *might* work as a common supertype; we've not validated that the required casts actually exist. In the mainstream use-cases that doesn't matter, because we'll proceed to invoke coerce_to_common_type() on each input, which will fail appropriately if the proposed common type doesn't actually work. However, a few callers didn't read the (nonexistent) fine print, and thought that if they got back a nonzero OID then the coercions were sure to work. This affects in particular the recently-added "anycompatible" polymorphic types; we might think that a function/operator using such types matches cases it really doesn't. A likely end result of that is unexpected "ambiguous operator" errors, as for example in bug #17387 from James Inform. Another, much older, case is that the parser might try to transform an "x IN (list)" construct to a ScalarArrayOpExpr even when the list elements don't actually have a common supertype. It doesn't seem desirable to add more checking to select_common_type itself, as that'd just slow down the mainstream use-cases. Instead, write a separate function verify_common_type that performs the missing checks, and add a call to that where necessary. Likewise add verify_common_type_from_oids to go with select_common_type_from_oids. Back-patch to v13 where the "anycompatible" types came in. (The symptom complained of in bug #17387 doesn't appear till v14, but that's just because we didn't get around to converting || to use anycompatible till then.) In principle the "x IN (list)" fix could go back all the way, but I'm not currently convinced that it makes much difference in real-world cases, so I won't bother for now. Discussion: https://postgr.es/m/17387-5dfe54b988444963@postgresql.org
This commit is contained in:
@@ -747,6 +747,18 @@ SELECT ARRAY[1.1] || ARRAY[2,3,4];
|
||||
{1.1,2,3,4}
|
||||
(1 row)
|
||||
|
||||
SELECT array_agg(x) || array_agg(x) FROM (VALUES (ROW(1,2)), (ROW(3,4))) v(x);
|
||||
?column?
|
||||
-----------------------------------
|
||||
{"(1,2)","(3,4)","(1,2)","(3,4)"}
|
||||
(1 row)
|
||||
|
||||
SELECT ROW(1,2) || array_agg(x) FROM (VALUES (ROW(3,4)), (ROW(5,6))) v(x);
|
||||
?column?
|
||||
---------------------------
|
||||
{"(1,2)","(3,4)","(5,6)"}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
|
||||
seqno | i | t
|
||||
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@@ -232,6 +232,36 @@ explain (verbose, costs off) select * from bpchar_view
|
||||
|
||||
rollback;
|
||||
--
|
||||
-- Ordinarily, IN/NOT IN can be converted to a ScalarArrayOpExpr
|
||||
-- with a suitably-chosen array type.
|
||||
--
|
||||
explain (verbose, costs off)
|
||||
select random() IN (1, 4, 8.0);
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------
|
||||
Result
|
||||
Output: (random() = ANY ('{1,4,8}'::double precision[]))
|
||||
(2 rows)
|
||||
|
||||
explain (verbose, costs off)
|
||||
select random()::int IN (1, 4, 8.0);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------
|
||||
Result
|
||||
Output: (((random())::integer)::numeric = ANY ('{1,4,8.0}'::numeric[]))
|
||||
(2 rows)
|
||||
|
||||
-- However, if there's not a common supertype for the IN elements,
|
||||
-- we should instead try to produce "x = v1 OR x = v2 OR ...".
|
||||
-- In most cases that'll fail for lack of all the requisite = operators,
|
||||
-- but it can succeed sometimes. So this should complain about lack of
|
||||
-- an = operator, not about cast failure.
|
||||
select '(0,0)'::point in ('(0,0,0,0)'::box, point(0,0));
|
||||
ERROR: operator does not exist: point = box
|
||||
LINE 1: select '(0,0)'::point in ('(0,0,0,0)'::box, point(0,0));
|
||||
^
|
||||
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
--
|
||||
-- Tests for ScalarArrayOpExpr with a hashfn
|
||||
--
|
||||
-- create a stable function so that the tests below are not
|
||||
|
@@ -317,6 +317,8 @@ SELECT ARRAY[[1,2],[3,4]] || ARRAY[5,6] AS "{{1,2},{3,4},{5,6}}";
|
||||
SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
|
||||
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
|
||||
SELECT ARRAY[1.1] || ARRAY[2,3,4];
|
||||
SELECT array_agg(x) || array_agg(x) FROM (VALUES (ROW(1,2)), (ROW(3,4))) v(x);
|
||||
SELECT ROW(1,2) || array_agg(x) FROM (VALUES (ROW(3,4)), (ROW(5,6))) v(x);
|
||||
|
||||
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
|
||||
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
|
||||
|
@@ -103,6 +103,22 @@ explain (verbose, costs off) select * from bpchar_view
|
||||
rollback;
|
||||
|
||||
|
||||
--
|
||||
-- Ordinarily, IN/NOT IN can be converted to a ScalarArrayOpExpr
|
||||
-- with a suitably-chosen array type.
|
||||
--
|
||||
explain (verbose, costs off)
|
||||
select random() IN (1, 4, 8.0);
|
||||
explain (verbose, costs off)
|
||||
select random()::int IN (1, 4, 8.0);
|
||||
-- However, if there's not a common supertype for the IN elements,
|
||||
-- we should instead try to produce "x = v1 OR x = v2 OR ...".
|
||||
-- In most cases that'll fail for lack of all the requisite = operators,
|
||||
-- but it can succeed sometimes. So this should complain about lack of
|
||||
-- an = operator, not about cast failure.
|
||||
select '(0,0)'::point in ('(0,0,0,0)'::box, point(0,0));
|
||||
|
||||
|
||||
--
|
||||
-- Tests for ScalarArrayOpExpr with a hashfn
|
||||
--
|
||||
|
Reference in New Issue
Block a user