mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
When to_char() formatted an integer value with a V pattern, it could
return an incorrect result instead of reporting an overflow. V shifts
the decimal point by multiplying the input value by a power of ten before
formatting it, so, for example,
to_char(3, '9V999999999')
requires computing 3 * 10^9. This result does not fit in int4, but
the integer variant of to_char() performed the multiplication using a
plain int32 expression. The intermediate result could therefore
overflow, causing the function to output incorrect digits instead of
raising "integer out of range".
Use dtoi4() and int4mul() for this calculation so that both an
out-of-range multiplier and an out-of-range product are detected, as
with ordinary integer arithmetic. This also matches the existing int8
implementation, which uses dtoi8() and int8mul() for the same
operation.
After this change, to_char() with V format either returns the
correctly formatted result when the scaled value fits in int4, or
raises "integer out of range" when it does not.
Backpatch to all supported versions.
Reported-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reviewed-by: Miłosz Bieniek <bieniek.milosz@proton.me>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAB8bMivEfqZxOVdzc3kZDN++XshmkEz2t7dfGBU8+oUm864EZg@mail.gmail.com
Backpatch-through: 14
188 lines
5.0 KiB
SQL
188 lines
5.0 KiB
SQL
--
|
|
-- INT4
|
|
--
|
|
|
|
CREATE TABLE INT4_TBL(f1 int4);
|
|
|
|
INSERT INTO INT4_TBL(f1) VALUES (' 0 ');
|
|
|
|
INSERT INTO INT4_TBL(f1) VALUES ('123456 ');
|
|
|
|
INSERT INTO INT4_TBL(f1) VALUES (' -123456');
|
|
|
|
INSERT INTO INT4_TBL(f1) VALUES ('34.5');
|
|
|
|
-- largest and smallest values
|
|
INSERT INTO INT4_TBL(f1) VALUES ('2147483647');
|
|
|
|
INSERT INTO INT4_TBL(f1) VALUES ('-2147483647');
|
|
|
|
-- bad input values -- should give errors
|
|
INSERT INTO INT4_TBL(f1) VALUES ('1000000000000');
|
|
INSERT INTO INT4_TBL(f1) VALUES ('asdf');
|
|
INSERT INTO INT4_TBL(f1) VALUES (' ');
|
|
INSERT INTO INT4_TBL(f1) VALUES (' asdf ');
|
|
INSERT INTO INT4_TBL(f1) VALUES ('- 1234');
|
|
INSERT INTO INT4_TBL(f1) VALUES ('123 5');
|
|
INSERT INTO INT4_TBL(f1) VALUES ('');
|
|
|
|
|
|
SELECT * FROM INT4_TBL;
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 <> int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 <> int4 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 = int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 = int4 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 < int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 < int4 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 <= int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 <= int4 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 > int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 > int4 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 >= int2 '0';
|
|
|
|
SELECT i.* FROM INT4_TBL i WHERE i.f1 >= int4 '0';
|
|
|
|
-- positive odds
|
|
SELECT i.* FROM INT4_TBL i WHERE (i.f1 % int2 '2') = int2 '1';
|
|
|
|
-- any evens
|
|
SELECT i.* FROM INT4_TBL i WHERE (i.f1 % int4 '2') = int2 '0';
|
|
|
|
SELECT i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 * int2 '2' AS x FROM INT4_TBL i
|
|
WHERE abs(f1) < 1073741824;
|
|
|
|
SELECT i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 * int4 '2' AS x FROM INT4_TBL i
|
|
WHERE abs(f1) < 1073741824;
|
|
|
|
SELECT i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 + int2 '2' AS x FROM INT4_TBL i
|
|
WHERE f1 < 2147483646;
|
|
|
|
SELECT i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 + int4 '2' AS x FROM INT4_TBL i
|
|
WHERE f1 < 2147483646;
|
|
|
|
SELECT i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 - int2 '2' AS x FROM INT4_TBL i
|
|
WHERE f1 > -2147483647;
|
|
|
|
SELECT i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 - int4 '2' AS x FROM INT4_TBL i
|
|
WHERE f1 > -2147483647;
|
|
|
|
SELECT i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
|
|
|
|
SELECT i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
|
|
|
|
--
|
|
-- more complex expressions
|
|
--
|
|
|
|
-- variations on unary minus parsing
|
|
SELECT -2+3 AS one;
|
|
|
|
SELECT 4-2 AS two;
|
|
|
|
SELECT 2- -1 AS three;
|
|
|
|
SELECT 2 - -2 AS four;
|
|
|
|
SELECT int2 '2' * int2 '2' = int2 '16' / int2 '4' AS true;
|
|
|
|
SELECT int4 '2' * int2 '2' = int2 '16' / int4 '4' AS true;
|
|
|
|
SELECT int2 '2' * int4 '2' = int4 '16' / int2 '4' AS true;
|
|
|
|
SELECT int4 '1000' < int4 '999' AS false;
|
|
|
|
SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten;
|
|
|
|
SELECT 2 + 2 / 2 AS three;
|
|
|
|
SELECT (2 + 2) / 2 AS two;
|
|
|
|
-- corner case
|
|
SELECT (-1::int4<<31)::text;
|
|
SELECT ((-1::int4<<31)+1)::text;
|
|
|
|
-- check sane handling of INT_MIN overflow cases
|
|
SELECT (-2147483648)::int4 * (-1)::int4;
|
|
SELECT (-2147483648)::int4 / (-1)::int4;
|
|
SELECT (-2147483648)::int4 % (-1)::int4;
|
|
SELECT (-2147483648)::int4 * (-1)::int2;
|
|
SELECT (-2147483648)::int4 / (-1)::int2;
|
|
SELECT (-2147483648)::int4 % (-1)::int2;
|
|
|
|
-- check overflow of to_char() with V format
|
|
SELECT to_char(2, '9V999999999'); -- 10^9
|
|
SELECT to_char(3, '9V999999999'); -- 10^9
|
|
SELECT to_char(214748364, '999999999V9');
|
|
SELECT to_char(2147483647, '9V9');
|
|
SELECT to_char(-2, '9V999999999'); -- 10^9
|
|
SELECT to_char((-2147483648)::int4, '9V9');
|
|
SELECT to_char(1, '9V9999999999'); -- 10^10
|
|
|
|
-- check rounding when casting from float
|
|
SELECT x, x::int4 AS int4_value
|
|
FROM (VALUES (-2.5::float8),
|
|
(-1.5::float8),
|
|
(-0.5::float8),
|
|
(0.0::float8),
|
|
(0.5::float8),
|
|
(1.5::float8),
|
|
(2.5::float8)) t(x);
|
|
|
|
-- check rounding when casting from numeric
|
|
SELECT x, x::int4 AS int4_value
|
|
FROM (VALUES (-2.5::numeric),
|
|
(-1.5::numeric),
|
|
(-0.5::numeric),
|
|
(0.0::numeric),
|
|
(0.5::numeric),
|
|
(1.5::numeric),
|
|
(2.5::numeric)) t(x);
|
|
|
|
-- test gcd()
|
|
SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a)
|
|
FROM (VALUES (0::int4, 0::int4),
|
|
(0::int4, 6410818::int4),
|
|
(61866666::int4, 6410818::int4),
|
|
(-61866666::int4, 6410818::int4),
|
|
((-2147483648)::int4, 1::int4),
|
|
((-2147483648)::int4, 2147483647::int4),
|
|
((-2147483648)::int4, 1073741824::int4)) AS v(a, b);
|
|
|
|
SELECT gcd((-2147483648)::int4, 0::int4); -- overflow
|
|
SELECT gcd((-2147483648)::int4, (-2147483648)::int4); -- overflow
|
|
|
|
-- test lcm()
|
|
SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a)
|
|
FROM (VALUES (0::int4, 0::int4),
|
|
(0::int4, 42::int4),
|
|
(42::int4, 42::int4),
|
|
(330::int4, 462::int4),
|
|
(-330::int4, 462::int4),
|
|
((-2147483648)::int4, 0::int4)) AS v(a, b);
|
|
|
|
SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
|
|
SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
|