]> granicus.if.org Git - postgresql/commitdiff
Make make_const() check the size and precision of a T_Float Value,
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 24 Feb 2000 01:59:17 +0000 (01:59 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 24 Feb 2000 01:59:17 +0000 (01:59 +0000)
and produce either FLOAT8 or NUMERIC output depending on whether the
value fits in a float8 or not.  This is almost back to the way the
code was before I changed T_Float, but there is a critical difference:
now, when a numeric constant doesn't fit in float8, it will be treated
as type NUMERIC instead of type UNKNOWN.

src/backend/parser/gram.y
src/backend/parser/parse_node.c

index d6fc0b0a625f18f54b894237a74628ccce9eac41..b14fde0799c6b4c534f2e972560ca3014295bd3c 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.149 2000/02/22 00:05:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.150 2000/02/24 01:59:17 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -5606,14 +5606,17 @@ Oid param_type(int t)
 }
 
 /*
- *     The optimizer doesn't like '-' 4 for index use.  It only checks for
- *     Var '=' Const.  It wants an integer of -4, so we try to merge the
- *     minus into the constant.
- *
- *     This code is no longer essential as of 10/1999, since the optimizer
- *     now has a constant-subexpression simplifier.  However, we can save
- *     a few cycles throughout the parse and rewrite stages if we collapse
- *     the minus into the constant sooner rather than later...
+ * doNegate --- handle negation of a numeric constant.
+ *
+ * Formerly, we did this here because the optimizer couldn't cope with
+ * indexquals that looked like "var = -4" --- it wants "var = const"
+ * and a unary minus operator applied to a constant didn't qualify.
+ * As of Postgres 7.0, that problem doesn't exist anymore because there
+ * is a constant-subexpression simplifier in the optimizer.  However,
+ * there's still a good reason for doing this here, which is that we can
+ * postpone committing to a particular internal representation for simple
+ * negative constants.  It's better to leave "-123.456" in string form
+ * until we know what the desired type is.
  */
 static Node *
 doNegate(Node *n)
index 19d2d11e5cd2b53502964e4adfd01f8f224244ae..2d365db91d5a5c0d39dca1157e6e09fcddf8c216 100644 (file)
@@ -8,13 +8,16 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.37 2000/01/26 05:56:42 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.38 2000/02/24 01:59:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <ctype.h>
+#include <errno.h>
+#include <float.h>
 
 #include "postgres.h"
+
 #include "access/heapam.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
@@ -32,6 +35,8 @@
 #include "utils/syscache.h"
 
 static void disallow_setop(char *op, Type optype, Node *operand);
+static bool fitsInFloat(Value *value);
+
 
 /* make_parsestate()
  * Allocate and initialize a new ParseState.
@@ -393,11 +398,25 @@ transformArraySubscripts(ParseState *pstate,
  * make_const
  *
  *     Convert a Value node (as returned by the grammar) to a Const node
- *     of the "natural" type for the constant.  For strings we produce
- *     a constant of type UNKNOWN ---- representation is the same as text,
- *     but this indicates to later type resolution that we're not sure that
- *     it should be considered text.  Explicit "NULL" constants are also
- *     typed as UNKNOWN.
+ *     of the "natural" type for the constant.  Note that this routine is
+ *     only used when there is no explicit cast for the constant, so we
+ *     have to guess what type is wanted.
+ *
+ *     For string literals we produce a constant of type UNKNOWN ---- whose
+ *     representation is the same as text, but it indicates to later type
+ *     resolution that we're not sure that it should be considered text.
+ *     Explicit "NULL" constants are also typed as UNKNOWN.
+ *
+ *  For integers and floats we produce int4, float8, or numeric depending
+ *     on the value of the number.  XXX In some cases it would be nice to take
+ *     context into account when determining the type to convert to, but in
+ *     other cases we can't delay the type choice.  One possibility is to invent
+ *     a dummy type "UNKNOWNNUMERIC" that's treated similarly to UNKNOWN;
+ *     that would allow us to do the right thing in examples like a simple
+ *     INSERT INTO table (numericcolumn) VALUES (1.234), since we wouldn't
+ *     have to resolve the unknown type until we knew the destination column
+ *     type.  On the other hand UNKNOWN has considerable problems of its own.
+ *     We would not like "SELECT 1.2 + 3.4" to claim it can't choose a type.
  */
 Const *
 make_const(Value *value)
@@ -419,18 +438,25 @@ make_const(Value *value)
                        break;
 
                case T_Float:
+                       if (fitsInFloat(value))
                        {
-                               float64         dummy;
+                               float64         fltval = (float64) palloc(sizeof(float64data));
 
-                               dummy = (float64) palloc(sizeof(float64data));
-                               *dummy = floatVal(value);
-
-                               val = Float64GetDatum(dummy);
+                               *fltval = floatVal(value);
+                               val = Float64GetDatum(fltval);
 
                                typeid = FLOAT8OID;
                                typelen = sizeof(float64data);
                                typebyval = false;
                        }
+                       else
+                       {
+                               val = PointerGetDatum(numeric_in(strVal(value), 0, -1));
+
+                               typeid = NUMERICOID;
+                               typelen = -1;   /* variable len */
+                               typebyval = false;
+                       }
                        break;
 
                case T_String:
@@ -441,11 +467,11 @@ make_const(Value *value)
                        typebyval = false;
                        break;
 
-               case T_Null:
                default:
-                       if (nodeTag(value) != T_Null)
-                               elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value));
+                       elog(NOTICE, "make_const: unknown type %d", nodeTag(value));
+                       /* FALLTHROUGH */
 
+               case T_Null:
                        /* return a null const */
                        con = makeConst(UNKNOWNOID,
                                                        -1,
@@ -467,3 +493,45 @@ make_const(Value *value)
 
        return con;
 }
+
+/*
+ * Decide whether a T_Float value fits in float8, or must be treated as
+ * type "numeric".  We check the number of digits and check for overflow/
+ * underflow.  (With standard compilation options, Postgres' NUMERIC type
+ * can handle decimal exponents up to 1000, considerably more than most
+ * implementations of float8, so this is a sensible test.)
+ */
+static bool
+fitsInFloat(Value *value)
+{
+       const char         *ptr;
+       int                             ndigits;
+       char               *endptr;
+
+       /*
+        * Count digits, ignoring leading zeroes (but not trailing zeroes).
+        * DBL_DIG is the maximum safe number of digits for "double".
+        */
+       ptr = strVal(value);
+       while (*ptr == '+' || *ptr == '-' || *ptr == '0' || *ptr == '.')
+               ptr++;
+       ndigits = 0;
+       for (; *ptr; ptr++)
+       {
+               if (isdigit(*ptr))
+                       ndigits++;
+               else if (*ptr == 'e' || *ptr == 'E')
+                       break;                          /* don't count digits in exponent */
+       }
+       if (ndigits > DBL_DIG)
+               return false;
+       /*
+        * Use strtod() to check for overflow/underflow.
+        */
+       errno = 0;
+       (void) strtod(strVal(value), &endptr);
+       if (*endptr != '\0' || errno != 0)
+               return false;
+
+       return true;
+}