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.
This commit is contained in:
Tom Lane 2000-02-24 01:59:17 +00:00
parent 399a570fe2
commit 512669db9e
2 changed files with 93 additions and 22 deletions

View 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.
* doNegate --- handle negation of a numeric 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...
* 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)

View 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;
}