1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

Improve contrib/cube's handling of zero-D cubes, infinities, and NaNs.

It's always been possible to create a zero-dimensional cube by converting
from a zero-length float8 array, but cube_in failed to accept the '()'
representation that cube_out produced for that case, resulting in a
dump/reload hazard.  Make it accept the case.  Also fix a couple of
other places that didn't behave sanely for zero-dimensional cubes:
cube_size would produce 1.0 when surely the answer should be 0.0,
and g_cube_distance risked a divide-by-zero failure.

Likewise, it's always been possible to create cubes containing float8
infinity or NaN coordinate values, but cube_in couldn't parse such input,
and cube_out produced platform-dependent spellings of the values.  Convert
them to use float8in_internal and float8out_internal so that the behavior
will be the same as for float8, as we recently did for the core geometric
types (cf commit 50861cd68).  As in that commit, I don't pretend that this
patch fixes all insane corner-case behaviors that may exist for NaNs, but
it's a step forward.

(This change allows removal of the separate cube_1.out and cube_3.out
expected-files, as the platform dependency that previously required them
is now gone: an underflowing coordinate value will now produce an error
not plus or minus zero.)

Make errors from cube_in follow project conventions as to spelling
("invalid input syntax for cube" not "bad cube representation")
and errcode (INVALID_TEXT_REPRESENTATION not SYNTAX_ERROR).

Also a few marginal code cleanups and comment improvements.

Tom Lane, reviewed by Amul Sul

Discussion: <15085.1472494782@sss.pgh.pa.us>
This commit is contained in:
Tom Lane
2016-09-27 11:38:33 -04:00
parent 51c3e9fade
commit f31a931fad
9 changed files with 275 additions and 3626 deletions

View File

@ -4,12 +4,13 @@
/* NdBox = [(lowerleft),(upperright)] */
/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
#define YYSTYPE char *
#define YYDEBUG 1
#include "postgres.h"
#include "cubedata.h"
#include "utils/builtins.h"
/* All grammar constructs return strings */
#define YYSTYPE char *
/*
* Bison doesn't allocate anything that needs to live across parser calls,
@ -25,9 +26,9 @@
static char *scanbuf;
static int scanbuflen;
static int delim_count(char *s, char delim);
static NDBOX * write_box(unsigned int dim, char *str1, char *str2);
static NDBOX * write_point_as_box(char *s, int dim);
static int item_count(const char *s, char delim);
static NDBOX *write_box(int dim, char *str1, char *str2);
static NDBOX *write_point_as_box(int dim, char *str);
%}
@ -46,47 +47,48 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
{
int dim;
dim = delim_count($2, ',') + 1;
if ((delim_count($4, ',') + 1) != dim)
dim = item_count($2, ',');
if (item_count($4, ',') != dim)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("Different point dimensions in (%s) and (%s).",
$2, $4)));
YYABORT;
}
if (dim > CUBE_MAX_DIM) {
if (dim > CUBE_MAX_DIM)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
*result = write_box( dim, $2, $4 );
}
| paren_list COMMA paren_list
{
int dim;
dim = delim_count($1, ',') + 1;
if ( (delim_count($3, ',') + 1) != dim ) {
dim = item_count($1, ',');
if (item_count($3, ',') != dim)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("Different point dimensions in (%s) and (%s).",
$1, $3)));
YYABORT;
}
if (dim > CUBE_MAX_DIM) {
if (dim > CUBE_MAX_DIM)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
@ -99,33 +101,36 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
{
int dim;
dim = delim_count($1, ',') + 1;
if (dim > CUBE_MAX_DIM) {
dim = item_count($1, ',');
if (dim > CUBE_MAX_DIM)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
*result = write_point_as_box($1, dim);
*result = write_point_as_box(dim, $1);
}
| list
{
int dim;
dim = delim_count($1, ',') + 1;
if (dim > CUBE_MAX_DIM) {
dim = item_count($1, ',');
if (dim > CUBE_MAX_DIM)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("bad cube representation"),
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
*result = write_point_as_box($1, dim);
*result = write_point_as_box(dim, $1);
}
;
@ -133,6 +138,10 @@ paren_list: O_PAREN list C_PAREN
{
$$ = $2;
}
| O_PAREN C_PAREN
{
$$ = pstrdup("");
}
;
list: CUBEFLOAT
@ -151,24 +160,30 @@ list: CUBEFLOAT
%%
/* This assumes the string has been normalized by productions above */
static int
delim_count(char *s, char delim)
item_count(const char *s, char delim)
{
int ndelim = 0;
int nitems = 0;
while ((s = strchr(s, delim)) != NULL)
if (s[0] != '\0')
{
ndelim++;
s++;
nitems++;
while ((s = strchr(s, delim)) != NULL)
{
nitems++;
s++;
}
}
return (ndelim);
return nitems;
}
static NDBOX *
write_box(unsigned int dim, char *str1, char *str2)
write_box(int dim, char *str1, char *str2)
{
NDBOX *bp;
char *s;
char *endptr;
int i;
int size = CUBE_SIZE(dim);
bool point = true;
@ -178,50 +193,58 @@ write_box(unsigned int dim, char *str1, char *str2)
SET_DIM(bp, dim);
s = str1;
bp->x[i=0] = strtod(s, NULL);
i = 0;
if (dim > 0)
bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
while ((s = strchr(s, ',')) != NULL)
{
s++; i++;
bp->x[i] = strtod(s, NULL);
s++;
bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
}
Assert(i == dim);
s = str2;
bp->x[i=dim] = strtod(s, NULL);
if (bp->x[dim] != bp->x[0])
point = false;
if (dim > 0)
{
bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
/* code this way to do right thing with NaN */
point &= (bp->x[i] == bp->x[0]);
i++;
}
while ((s = strchr(s, ',')) != NULL)
{
s++; i++;
bp->x[i] = strtod(s, NULL);
if (bp->x[i] != bp->x[i-dim])
point = false;
s++;
bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
point &= (bp->x[i] == bp->x[i - dim]);
i++;
}
Assert(i == dim * 2);
if (point)
{
/*
* The value turned out to be a point, ie. all the upper-right
* coordinates were equal to the lower-left coordinates. Resize the
* the cube we constructed. Note: we don't bother to repalloc() it
* smaller, it's unlikely that the tiny amount of memory free'd that
* way would be useful.
* cube we constructed. Note: we don't bother to repalloc() it
* smaller, as it's unlikely that the tiny amount of memory freed
* that way would be useful, and the output is always short-lived.
*/
size = POINT_SIZE(dim);
SET_VARSIZE(bp, size);
SET_POINT_BIT(bp);
}
return(bp);
return bp;
}
static NDBOX *
write_point_as_box(char *str, int dim)
write_point_as_box(int dim, char *str)
{
NDBOX *bp;
int i,
size;
double x;
char *s = str;
char *s;
char *endptr;
size = POINT_SIZE(dim);
bp = palloc0(size);
@ -229,17 +252,18 @@ write_point_as_box(char *str, int dim)
SET_DIM(bp, dim);
SET_POINT_BIT(bp);
s = str;
i = 0;
x = strtod(s, NULL);
bp->x[0] = x;
if (dim > 0)
bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
while ((s = strchr(s, ',')) != NULL)
{
s++; i++;
x = strtod(s, NULL);
bp->x[i] = x;
s++;
bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
}
Assert(i == dim);
return(bp);
return bp;
}
#include "cubescan.c"