Skip to content

Commit 512669d

Browse files
committed
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.
1 parent 399a570 commit 512669d

File tree

2 files changed

+94
-23
lines changed

2 files changed

+94
-23
lines changed

src/backend/parser/gram.y

+12-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.149 2000/02/22 00:05:04 tgl Exp $
14+
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.150 2000/02/24 01:59:17 tgl Exp $
1515
*
1616
* HISTORY
1717
* AUTHOR DATE MAJOR EVENT
@@ -5606,14 +5606,17 @@ Oid param_type(int t)
56065606
}
56075607

56085608
/*
5609-
* The optimizer doesn't like '-' 4 for index use. It only checks for
5610-
* Var '=' Const. It wants an integer of -4, so we try to merge the
5611-
* minus into the constant.
5612-
*
5613-
* This code is no longer essential as of 10/1999, since the optimizer
5614-
* now has a constant-subexpression simplifier. However, we can save
5615-
* a few cycles throughout the parse and rewrite stages if we collapse
5616-
* the minus into the constant sooner rather than later...
5609+
* doNegate --- handle negation of a numeric constant.
5610+
*
5611+
* Formerly, we did this here because the optimizer couldn't cope with
5612+
* indexquals that looked like "var = -4" --- it wants "var = const"
5613+
* and a unary minus operator applied to a constant didn't qualify.
5614+
* As of Postgres 7.0, that problem doesn't exist anymore because there
5615+
* is a constant-subexpression simplifier in the optimizer. However,
5616+
* there's still a good reason for doing this here, which is that we can
5617+
* postpone committing to a particular internal representation for simple
5618+
* negative constants. It's better to leave "-123.456" in string form
5619+
* until we know what the desired type is.
56175620
*/
56185621
static Node *
56195622
doNegate(Node *n)

src/backend/parser/parse_node.c

+82-14
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.37 2000/01/26 05:56:42 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.38 2000/02/24 01:59:17 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
#include <ctype.h>
16+
#include <errno.h>
17+
#include <float.h>
1618

1719
#include "postgres.h"
20+
1821
#include "access/heapam.h"
1922
#include "catalog/pg_operator.h"
2023
#include "catalog/pg_type.h"
@@ -32,6 +35,8 @@
3235
#include "utils/syscache.h"
3336

3437
static void disallow_setop(char *op, Type optype, Node *operand);
38+
static bool fitsInFloat(Value *value);
39+
3540

3641
/* make_parsestate()
3742
* Allocate and initialize a new ParseState.
@@ -393,11 +398,25 @@ transformArraySubscripts(ParseState *pstate,
393398
* make_const
394399
*
395400
* Convert a Value node (as returned by the grammar) to a Const node
396-
* of the "natural" type for the constant. For strings we produce
397-
* a constant of type UNKNOWN ---- representation is the same as text,
398-
* but this indicates to later type resolution that we're not sure that
399-
* it should be considered text. Explicit "NULL" constants are also
400-
* typed as UNKNOWN.
401+
* of the "natural" type for the constant. Note that this routine is
402+
* only used when there is no explicit cast for the constant, so we
403+
* have to guess what type is wanted.
404+
*
405+
* For string literals we produce a constant of type UNKNOWN ---- whose
406+
* representation is the same as text, but it indicates to later type
407+
* resolution that we're not sure that it should be considered text.
408+
* Explicit "NULL" constants are also typed as UNKNOWN.
409+
*
410+
* For integers and floats we produce int4, float8, or numeric depending
411+
* on the value of the number. XXX In some cases it would be nice to take
412+
* context into account when determining the type to convert to, but in
413+
* other cases we can't delay the type choice. One possibility is to invent
414+
* a dummy type "UNKNOWNNUMERIC" that's treated similarly to UNKNOWN;
415+
* that would allow us to do the right thing in examples like a simple
416+
* INSERT INTO table (numericcolumn) VALUES (1.234), since we wouldn't
417+
* have to resolve the unknown type until we knew the destination column
418+
* type. On the other hand UNKNOWN has considerable problems of its own.
419+
* We would not like "SELECT 1.2 + 3.4" to claim it can't choose a type.
401420
*/
402421
Const *
403422
make_const(Value *value)
@@ -419,18 +438,25 @@ make_const(Value *value)
419438
break;
420439

421440
case T_Float:
441+
if (fitsInFloat(value))
422442
{
423-
float64 dummy;
443+
float64 fltval = (float64) palloc(sizeof(float64data));
424444

425-
dummy = (float64) palloc(sizeof(float64data));
426-
*dummy = floatVal(value);
427-
428-
val = Float64GetDatum(dummy);
445+
*fltval = floatVal(value);
446+
val = Float64GetDatum(fltval);
429447

430448
typeid = FLOAT8OID;
431449
typelen = sizeof(float64data);
432450
typebyval = false;
433451
}
452+
else
453+
{
454+
val = PointerGetDatum(numeric_in(strVal(value), 0, -1));
455+
456+
typeid = NUMERICOID;
457+
typelen = -1; /* variable len */
458+
typebyval = false;
459+
}
434460
break;
435461

436462
case T_String:
@@ -441,11 +467,11 @@ make_const(Value *value)
441467
typebyval = false;
442468
break;
443469

444-
case T_Null:
445470
default:
446-
if (nodeTag(value) != T_Null)
447-
elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value));
471+
elog(NOTICE, "make_const: unknown type %d", nodeTag(value));
472+
/* FALLTHROUGH */
448473

474+
case T_Null:
449475
/* return a null const */
450476
con = makeConst(UNKNOWNOID,
451477
-1,
@@ -467,3 +493,45 @@ make_const(Value *value)
467493

468494
return con;
469495
}
496+
497+
/*
498+
* Decide whether a T_Float value fits in float8, or must be treated as
499+
* type "numeric". We check the number of digits and check for overflow/
500+
* underflow. (With standard compilation options, Postgres' NUMERIC type
501+
* can handle decimal exponents up to 1000, considerably more than most
502+
* implementations of float8, so this is a sensible test.)
503+
*/
504+
static bool
505+
fitsInFloat(Value *value)
506+
{
507+
const char *ptr;
508+
int ndigits;
509+
char *endptr;
510+
511+
/*
512+
* Count digits, ignoring leading zeroes (but not trailing zeroes).
513+
* DBL_DIG is the maximum safe number of digits for "double".
514+
*/
515+
ptr = strVal(value);
516+
while (*ptr == '+' || *ptr == '-' || *ptr == '0' || *ptr == '.')
517+
ptr++;
518+
ndigits = 0;
519+
for (; *ptr; ptr++)
520+
{
521+
if (isdigit(*ptr))
522+
ndigits++;
523+
else if (*ptr == 'e' || *ptr == 'E')
524+
break; /* don't count digits in exponent */
525+
}
526+
if (ndigits > DBL_DIG)
527+
return false;
528+
/*
529+
* Use strtod() to check for overflow/underflow.
530+
*/
531+
errno = 0;
532+
(void) strtod(strVal(value), &endptr);
533+
if (*endptr != '\0' || errno != 0)
534+
return false;
535+
536+
return true;
537+
}

0 commit comments

Comments
 (0)