diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index 4a52a9399a5..abbd9eef4e8 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -78,7 +78,7 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; ---------------------------------------------------- Limit -> Index Only Scan using float4idx on float4tmp - Order By: (a <-> (-179)::real) + Order By: (a <-> '-179'::real) (3 rows) SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index 918d84860f5..5111dbdfaea 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -78,7 +78,7 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; ----------------------------------------------------- Limit -> Index Only Scan using float8idx on float8tmp - Order By: (a <-> (-1890)::double precision) + Order By: (a <-> '-1890'::double precision) (3 rows) SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 711e2c722ac..50a332939bd 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -78,7 +78,7 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; ------------------------------------------------ Limit -> Index Only Scan using int2idx on int2tmp - Order By: (a <-> 237::smallint) + Order By: (a <-> '237'::smallint) (3 rows) SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index 55982ee3b5c..eff77c26b5a 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -74,11 +74,11 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841::int8; EXPLAIN (COSTS OFF) SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +----------------------------------------------------- Limit -> Index Only Scan using int8idx on int8tmp - Order By: (a <-> 464571291354841::bigint) + Order By: (a <-> '464571291354841'::bigint) (3 rows) SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index b9b67b503a2..ae839b8ec83 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -190,12 +190,12 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; SET enable_bitmapscan=off; EXPLAIN (COSTS OFF) SELECT * FROM numerictmp WHERE a BETWEEN 1 AND 300 ORDER BY a; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Sort Sort Key: a -> Index Only Scan using numericidx on numerictmp - Index Cond: ((a >= 1::numeric) AND (a <= 300::numeric)) + Index Cond: ((a >= '1'::numeric) AND (a <= '300'::numeric)) (4 rows) SELECT * FROM numerictmp WHERE a BETWEEN 1 AND 300 ORDER BY a; diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 4207fb34d05..783cb41571d 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -874,7 +874,7 @@ SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1; Output: c1, c2, c3, c4, c5, c6, c7, c8 -> Foreign Scan on public.ft1 t1 Output: c1, c2, c3, c4, c5, c6, c7, c8 - Filter: (t1.tableoid = 1259::oid) + Filter: (t1.tableoid = '1259'::oid) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" (6 rows) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 28e1acfb86a..29b5b1b8945 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -15,6 +15,7 @@ */ #include "postgres.h" +#include #include #include @@ -7977,6 +7978,13 @@ get_coercion_expr(Node *arg, deparse_context *context, * right above it. Avoid generating redundant output. However, beware of * suppressing casts when the user actually wrote something like * 'foo'::text::char(3). + * + * Note: it might seem that we are missing the possibility of needing to + * print a COLLATE clause for such a Const. However, a Const could only + * have nondefault collation in a post-constant-folding tree, in which the + * length coercion would have been folded too. See also the special + * handling of CollateExpr in coerce_to_target_type(): any collation + * marking will be above the coercion node, not below it. */ if (arg && IsA(arg, Const) && ((Const *) arg)->consttype == resulttype && @@ -8007,8 +8015,9 @@ get_coercion_expr(Node *arg, deparse_context *context, * the right type by default. * * If the Const's collation isn't default for its type, show that too. - * This can only happen in trees that have been through constant-folding. - * We assume we don't need to do this when showtype is -1. + * We mustn't do this when showtype is -1 (since that means the caller will + * print "::typename", and we can't put a COLLATE clause in between). It's + * caller's responsibility that collation isn't missed in such cases. * ---------- */ static void @@ -8018,8 +8027,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) Oid typoutput; bool typIsVarlena; char *extval; - bool isfloat = false; - bool needlabel; + bool needlabel = false; if (constval->constisnull) { @@ -8045,40 +8053,42 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) switch (constval->consttype) { - case INT2OID: case INT4OID: - case INT8OID: - case OIDOID: - case FLOAT4OID: - case FLOAT8OID: - case NUMERICOID: + + /* + * INT4 can be printed without any decoration, unless it is + * negative; in that case print it as '-nnn'::integer to ensure + * that the output will re-parse as a constant, not as a constant + * plus operator. In most cases we could get away with printing + * (-nnn) instead, because of the way that gram.y handles negative + * literals; but that doesn't work for INT_MIN, and it doesn't + * seem that much prettier anyway. + */ + if (extval[0] != '-') + appendStringInfoString(buf, extval); + else { - /* - * These types are printed without quotes unless they contain - * values that aren't accepted by the scanner unquoted (e.g., - * 'NaN'). Note that strtod() and friends might accept NaN, - * so we can't use that to test. - * - * In reality we only need to defend against infinity and NaN, - * so we need not get too crazy about pattern matching here. - * - * There is a special-case gotcha: if the constant is signed, - * we need to parenthesize it, else the parser might see a - * leading plus/minus as binding less tightly than adjacent - * operators --- particularly, the cast that we might attach - * below. - */ - if (strspn(extval, "0123456789+-eE.") == strlen(extval)) - { - if (extval[0] == '+' || extval[0] == '-') - appendStringInfo(buf, "(%s)", extval); - else - appendStringInfoString(buf, extval); - if (strcspn(extval, "eE.") != strlen(extval)) - isfloat = true; /* it looks like a float */ - } - else - appendStringInfo(buf, "'%s'", extval); + appendStringInfo(buf, "'%s'", extval); + needlabel = true; /* we must attach a cast */ + } + break; + + case NUMERICOID: + + /* + * NUMERIC can be printed without quotes if it looks like a float + * constant (not an integer, and not Infinity or NaN) and doesn't + * have a leading sign (for the same reason as for INT4). + */ + if (isdigit((unsigned char) extval[0]) && + strcspn(extval, "eE.") != strlen(extval)) + { + appendStringInfoString(buf, extval); + } + else + { + appendStringInfo(buf, "'%s'", extval); + needlabel = true; /* we must attach a cast */ } break; @@ -8114,18 +8124,21 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) switch (constval->consttype) { case BOOLOID: - case INT4OID: case UNKNOWNOID: /* These types can be left unlabeled */ needlabel = false; break; + case INT4OID: + /* We determined above whether a label is needed */ + break; case NUMERICOID: /* - * Float-looking constants will be typed as numeric, but if - * there's a specific typmod we need to show it. + * Float-looking constants will be typed as numeric, which we + * checked above; but if there's a nondefault typmod we need to + * show it. */ - needlabel = !isfloat || (constval->consttypmod >= 0); + needlabel |= (constval->consttypmod >= 0); break; default: needlabel = true; diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out index dfae84e1a5f..0391b8eec19 100644 --- a/src/test/regress/expected/equivclass.out +++ b/src/test/regress/expected/equivclass.out @@ -104,11 +104,11 @@ set enable_mergejoin = off; -- explain (costs off) select * from ec0 where ff = f1 and f1 = '42'::int8; - QUERY PLAN ----------------------------------- + QUERY PLAN +----------------------------------- Index Scan using ec0_pkey on ec0 - Index Cond: (ff = 42::bigint) - Filter: (f1 = 42::bigint) + Index Cond: (ff = '42'::bigint) + Filter: (f1 = '42'::bigint) (3 rows) explain (costs off) @@ -139,12 +139,12 @@ explain (costs off) explain (costs off) select * from ec1, ec2 where ff = x1 and ff = '42'::int8; - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Nested Loop Join Filter: (ec1.ff = ec2.x1) -> Index Scan using ec1_pkey on ec1 - Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint)) + Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint)) -> Seq Scan on ec2 (5 rows) @@ -161,14 +161,14 @@ explain (costs off) explain (costs off) select * from ec1, ec2 where ff = x1 and '42'::int8 = x1; - QUERY PLAN ----------------------------------------- + QUERY PLAN +----------------------------------------- Nested Loop Join Filter: (ec1.ff = ec2.x1) -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) -> Seq Scan on ec2 - Filter: (42::bigint = x1) + Filter: ('42'::bigint = x1) (6 rows) explain (costs off) @@ -210,7 +210,7 @@ explain (costs off) ----------------------------------------------------- Nested Loop -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) -> Append -> Index Scan using ec1_expr2 on ec1 ec1_1 Index Cond: (((ff + 2) + 1) = ec1.f1) @@ -229,20 +229,20 @@ explain (costs off) union all select ff + 4 as x from ec1) as ss1 where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1; - QUERY PLAN ---------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Nested Loop Join Filter: ((((ec1_1.ff + 2) + 1)) = ec1.f1) -> Index Scan using ec1_pkey on ec1 - Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint)) + Index Cond: ((ff = '42'::bigint) AND (ff = '42'::bigint)) Filter: (ff = f1) -> Append -> Index Scan using ec1_expr2 on ec1 ec1_1 - Index Cond: (((ff + 2) + 1) = 42::bigint) + Index Cond: (((ff + 2) + 1) = '42'::bigint) -> Index Scan using ec1_expr3 on ec1 ec1_2 - Index Cond: (((ff + 3) + 1) = 42::bigint) + Index Cond: (((ff + 3) + 1) = '42'::bigint) -> Index Scan using ec1_expr4 on ec1 ec1_3 - Index Cond: ((ff + 4) = 42::bigint) + Index Cond: ((ff + 4) = '42'::bigint) (12 rows) explain (costs off) @@ -265,7 +265,7 @@ explain (costs off) Nested Loop -> Nested Loop -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) -> Append -> Index Scan using ec1_expr2 on ec1 ec1_1 Index Cond: (((ff + 2) + 1) = ec1.f1) @@ -321,7 +321,7 @@ explain (costs off) -> Sort Sort Key: ec1.f1 USING < -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) (20 rows) -- check partially indexed scan @@ -341,7 +341,7 @@ explain (costs off) ----------------------------------------------------- Nested Loop -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) -> Append -> Index Scan using ec1_expr2 on ec1 ec1_1 Index Cond: (((ff + 2) + 1) = ec1.f1) @@ -378,6 +378,6 @@ explain (costs off) -> Sort Sort Key: ec1.f1 USING < -> Index Scan using ec1_pkey on ec1 - Index Cond: (ff = 42::bigint) + Index Cond: (ff = '42'::bigint) (14 rows) diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 57fc910133b..046b0853012 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -2544,16 +2544,16 @@ SELECT qq, unique1 ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 USING (qq) INNER JOIN tenk1 c ON qq = unique2; - QUERY PLAN -------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------- Nested Loop -> Hash Full Join - Hash Cond: (COALESCE(a.q1, 0::bigint) = COALESCE(b.q2, (-1)::bigint)) + Hash Cond: (COALESCE(a.q1, '0'::bigint) = COALESCE(b.q2, '-1'::bigint)) -> Seq Scan on int8_tbl a -> Hash -> Seq Scan on int8_tbl b -> Index Scan using tenk1_unique2 on tenk1 c - Index Cond: (unique2 = COALESCE((COALESCE(a.q1, 0::bigint)), (COALESCE(b.q2, (-1)::bigint)))) + Index Cond: (unique2 = COALESCE((COALESCE(a.q1, '0'::bigint)), (COALESCE(b.q2, '-1'::bigint)))) (8 rows) SELECT qq, unique1 @@ -3003,10 +3003,10 @@ select * from ) ss where fault = 122 order by fault; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------------------- Nested Loop Left Join - Filter: ((COALESCE(tenk1.unique1, (-1)) + int8_tbl.q1) = 122) + Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122) -> Seq Scan on int8_tbl -> Index Scan using tenk1_unique2 on tenk1 Index Cond: (int8_tbl.q2 = unique2) @@ -4012,14 +4012,14 @@ explain (verbose, costs off) select * from int8_tbl a left join lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; - QUERY PLAN ----------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------ Nested Loop Left Join - Output: a.q1, a.q2, b.q1, b.q2, (COALESCE(a.q2, 42::bigint)) + Output: a.q1, a.q2, b.q1, b.q2, (COALESCE(a.q2, '42'::bigint)) -> Seq Scan on public.int8_tbl a Output: a.q1, a.q2 -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2, COALESCE(a.q2, 42::bigint) + Output: b.q1, b.q2, COALESCE(a.q2, '42'::bigint) Filter: (a.q2 = b.q1) (7 rows) @@ -4235,32 +4235,32 @@ select * from lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 ) on c.q2 = ss2.q1, lateral (select ss2.y offset 0) ss3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Nested Loop - Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)), d.q1, (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)), ((COALESCE((COALESCE(b.q2, 42::bigint)), d.q2))) + Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)), d.q1, (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)), ((COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2))) -> Hash Right Join - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)) + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, '42'::bigint)), (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) Hash Cond: (d.q1 = c.q2) -> Nested Loop - Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)) + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, '42'::bigint)), (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) -> Hash Left Join - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)) + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)) Hash Cond: (a.q2 = b.q1) -> Seq Scan on public.int8_tbl a Output: a.q1, a.q2 -> Hash - Output: b.q1, (COALESCE(b.q2, 42::bigint)) + Output: b.q1, (COALESCE(b.q2, '42'::bigint)) -> Seq Scan on public.int8_tbl b - Output: b.q1, COALESCE(b.q2, 42::bigint) + Output: b.q1, COALESCE(b.q2, '42'::bigint) -> Seq Scan on public.int8_tbl d - Output: d.q1, COALESCE((COALESCE(b.q2, 42::bigint)), d.q2) + Output: d.q1, COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2) -> Hash Output: c.q1, c.q2 -> Seq Scan on public.int8_tbl c Output: c.q1, c.q2 -> Result - Output: (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)) + Output: (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) (24 rows) -- case that breaks the old ph_may_need optimization diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 54525de6b1d..d3a98d126cd 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -284,10 +284,10 @@ ERROR: cannot compare dissimilar column types bigint and integer at record colu explain (costs off) select * from int8_tbl i8 where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)'); - QUERY PLAN -------------------------------------------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- Seq Scan on int8_tbl i8 - Filter: (i8.* = ANY (ARRAY[ROW(123::bigint, 456::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl])) + Filter: (i8.* = ANY (ARRAY[ROW('123'::bigint, '456'::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl])) (2 rows) select * from int8_tbl i8 diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out index a678e960d49..016571bd4a9 100644 --- a/src/test/regress/expected/union.out +++ b/src/test/regress/expected/union.out @@ -654,13 +654,13 @@ SELECT * FROM UNION SELECT 2 AS t, 4 AS x) ss WHERE x > 3; - QUERY PLAN ----------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------ Subquery Scan on ss Filter: (ss.x > 3) -> Unique -> Sort - Sort Key: (1), (((random() * 3::double precision))::integer) + Sort Key: (1), (((random() * '3'::double precision))::integer) -> Append -> Result -> Result diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 6986f4777ef..a31ec341e69 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -2083,8 +2083,8 @@ SELECT * FROM parent; EXPLAIN (VERBOSE, COSTS OFF) WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) DELETE FROM a USING wcte WHERE aa = q2; - QUERY PLAN ------------------------------------------------- + QUERY PLAN +---------------------------------------------------- Delete on public.a Delete on public.a Delete on public.b @@ -2094,7 +2094,7 @@ DELETE FROM a USING wcte WHERE aa = q2; -> Insert on public.int8_tbl Output: int8_tbl.q2 -> Result - Output: 42::bigint, 47::bigint + Output: '42'::bigint, '47'::bigint -> Nested Loop Output: a.ctid, wcte.* Join Filter: (a.aa = wcte.q2)