Commit 512669db authored by Tom Lane's avatar Tom Lane

Make make_const() check the size and precision of a T_Float Value,

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.
parent 399a570f
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -5606,14 +5606,17 @@ Oid param_type(int t) ...@@ -5606,14 +5606,17 @@ Oid param_type(int t)
} }
/* /*
* The optimizer doesn't like '-' 4 for index use. It only checks for * doNegate --- handle negation of a numeric constant.
* Var '=' Const. It wants an integer of -4, so we try to merge the *
* minus into the constant. * Formerly, we did this here because the optimizer couldn't cope with
* * indexquals that looked like "var = -4" --- it wants "var = const"
* This code is no longer essential as of 10/1999, since the optimizer * and a unary minus operator applied to a constant didn't qualify.
* now has a constant-subexpression simplifier. However, we can save * As of Postgres 7.0, that problem doesn't exist anymore because there
* a few cycles throughout the parse and rewrite stages if we collapse * is a constant-subexpression simplifier in the optimizer. However,
* the minus into the constant sooner rather than later... * 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 * static Node *
doNegate(Node *n) doNegate(Node *n)
......
...@@ -8,13 +8,16 @@ ...@@ -8,13 +8,16 @@
* *
* *
* IDENTIFICATION * 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 <ctype.h>
#include <errno.h>
#include <float.h>
#include "postgres.h" #include "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
...@@ -32,6 +35,8 @@ ...@@ -32,6 +35,8 @@
#include "utils/syscache.h" #include "utils/syscache.h"
static void disallow_setop(char *op, Type optype, Node *operand); static void disallow_setop(char *op, Type optype, Node *operand);
static bool fitsInFloat(Value *value);
/* make_parsestate() /* make_parsestate()
* Allocate and initialize a new ParseState. * Allocate and initialize a new ParseState.
...@@ -393,11 +398,25 @@ transformArraySubscripts(ParseState *pstate, ...@@ -393,11 +398,25 @@ transformArraySubscripts(ParseState *pstate,
* make_const * make_const
* *
* Convert a Value node (as returned by the grammar) to a Const node * Convert a Value node (as returned by the grammar) to a Const node
* of the "natural" type for the constant. For strings we produce * of the "natural" type for the constant. Note that this routine is
* a constant of type UNKNOWN ---- representation is the same as text, * only used when there is no explicit cast for the constant, so we
* but this indicates to later type resolution that we're not sure that * have to guess what type is wanted.
* it should be considered text. Explicit "NULL" constants are also *
* typed as UNKNOWN. * 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 * Const *
make_const(Value *value) make_const(Value *value)
...@@ -419,18 +438,25 @@ make_const(Value *value) ...@@ -419,18 +438,25 @@ make_const(Value *value)
break; break;
case T_Float: case T_Float:
if (fitsInFloat(value))
{ {
float64 dummy; float64 fltval = (float64) palloc(sizeof(float64data));
dummy = (float64) palloc(sizeof(float64data)); *fltval = floatVal(value);
*dummy = floatVal(value); val = Float64GetDatum(fltval);
val = Float64GetDatum(dummy);
typeid = FLOAT8OID; typeid = FLOAT8OID;
typelen = sizeof(float64data); typelen = sizeof(float64data);
typebyval = false; typebyval = false;
} }
else
{
val = PointerGetDatum(numeric_in(strVal(value), 0, -1));
typeid = NUMERICOID;
typelen = -1; /* variable len */
typebyval = false;
}
break; break;
case T_String: case T_String:
...@@ -441,11 +467,11 @@ make_const(Value *value) ...@@ -441,11 +467,11 @@ make_const(Value *value)
typebyval = false; typebyval = false;
break; break;
case T_Null:
default: default:
if (nodeTag(value) != T_Null) elog(NOTICE, "make_const: unknown type %d", nodeTag(value));
elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value)); /* FALLTHROUGH */
case T_Null:
/* return a null const */ /* return a null const */
con = makeConst(UNKNOWNOID, con = makeConst(UNKNOWNOID,
-1, -1,
...@@ -467,3 +493,45 @@ make_const(Value *value) ...@@ -467,3 +493,45 @@ make_const(Value *value)
return con; 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment