diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2015-03-30 14:59:49 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2015-03-30 14:59:49 -0400 |
commit | 542320c2bd0b3796a8a9a4617cdb23fbad473390 (patch) | |
tree | 38a517f89bfa0098960d44fae94829a3b86ce3bd /src/backend/utils/adt/ruleutils.c | |
parent | 701dcc983eb4d08dd36bb3a0ddba255819797760 (diff) | |
download | postgresql-542320c2bd0b3796a8a9a4617cdb23fbad473390.tar.gz postgresql-542320c2bd0b3796a8a9a4617cdb23fbad473390.zip |
Be more careful about printing constants in ruleutils.c.
The previous coding in get_const_expr() tried to avoid quoting integer,
float, and numeric literals if at all possible. While that looks nice,
it means that dumped expressions might re-parse to something that's
semantically equivalent but not the exact same parsetree; for example
a FLOAT8 constant would re-parse as a NUMERIC constant with a cast to
FLOAT8. Though the result would be the same after constant-folding,
this is problematic in certain contexts. In particular, Jeff Davis
pointed out that this could cause unexpected failures in ALTER INHERIT
operations because of child tables having not-exactly-equivalent CHECK
expressions. Therefore, favor correctness over legibility and dump
such constants in quotes except in the limited cases where they'll
be interpreted as the same type even without any casting.
This results in assorted small changes in the regression test outputs,
and will affect display of user-defined views and rules similarly.
The odds of that causing problems in the field seem non-negligible;
given the lack of previous complaints, it seems best not to change
this in the back branches.
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 91 |
1 files changed, 52 insertions, 39 deletions
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 <ctype.h> #include <unistd.h> #include <fcntl.h> @@ -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: + + /* + * 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 + { + 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)) { - /* - * 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); + 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; |