postgresql/contrib/cube/cubeparse.y
Tom Lane ccff2d20ed Convert a few datatype input functions to use "soft" error reporting.
This patch converts the input functions for bool, int2, int4, int8,
float4, float8, numeric, and contrib/cube to the new soft-error style.
array_in and record_in are also converted.  There's lots more to do,
but this is enough to provide proof-of-concept that the soft-error
API is usable, as well as reference examples for how to convert
input functions.

This patch is mostly by me, but it owes very substantial debt to
earlier work by Nikita Glukhov, Andrew Dunstan, and Amul Sul.
Thanks to Andres Freund for review.

Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
2022-12-09 10:14:53 -05:00

291 lines
5.9 KiB
Plaintext

%{
/* contrib/cube/cubeparse.y */
/* NdBox = [(lowerleft),(upperright)] */
/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
#include "postgres.h"
#include "cubedata.h"
#include "nodes/miscnodes.h"
#include "utils/float.h"
/* All grammar constructs return strings */
#define YYSTYPE char *
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
* memory leaks if we error out during parsing.
*/
#define YYMALLOC palloc
#define YYFREE pfree
static int item_count(const char *s, char delim);
static bool write_box(int dim, char *str1, char *str2,
NDBOX **result, struct Node *escontext);
static bool write_point_as_box(int dim, char *str,
NDBOX **result, struct Node *escontext);
%}
/* BISON Declarations */
%parse-param {NDBOX **result}
%parse-param {Size scanbuflen}
%parse-param {struct Node *escontext}
%expect 0
%name-prefix="cube_yy"
%token CUBEFLOAT O_PAREN C_PAREN O_BRACKET C_BRACKET COMMA
%start box
/* Grammar follows */
%%
box: O_BRACKET paren_list COMMA paren_list C_BRACKET
{
int dim;
dim = item_count($2, ',');
if (item_count($4, ',') != dim)
{
errsave(escontext,
(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)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
if (!write_box(dim, $2, $4, result, escontext))
YYABORT;
}
| paren_list COMMA paren_list
{
int dim;
dim = item_count($1, ',');
if (item_count($3, ',') != dim)
{
errsave(escontext,
(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)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
if (!write_box(dim, $1, $3, result, escontext))
YYABORT;
}
| paren_list
{
int dim;
dim = item_count($1, ',');
if (dim > CUBE_MAX_DIM)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
if (!write_point_as_box(dim, $1, result, escontext))
YYABORT;
}
| list
{
int dim;
dim = item_count($1, ',');
if (dim > CUBE_MAX_DIM)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
errdetail("A cube cannot have more than %d dimensions.",
CUBE_MAX_DIM)));
YYABORT;
}
if (!write_point_as_box(dim, $1, result, escontext))
YYABORT;
}
;
paren_list: O_PAREN list C_PAREN
{
$$ = $2;
}
| O_PAREN C_PAREN
{
$$ = pstrdup("");
}
;
list: CUBEFLOAT
{
/* alloc enough space to be sure whole list will fit */
$$ = palloc(scanbuflen + 1);
strcpy($$, $1);
}
| list COMMA CUBEFLOAT
{
$$ = $1;
strcat($$, ",");
strcat($$, $3);
}
;
%%
/* This assumes the string has been normalized by productions above */
static int
item_count(const char *s, char delim)
{
int nitems = 0;
if (s[0] != '\0')
{
nitems++;
while ((s = strchr(s, delim)) != NULL)
{
nitems++;
s++;
}
}
return nitems;
}
static bool
write_box(int dim, char *str1, char *str2,
NDBOX **result, struct Node *escontext)
{
NDBOX *bp;
char *s;
char *endptr;
int i;
int size = CUBE_SIZE(dim);
bool point = true;
bp = palloc0(size);
SET_VARSIZE(bp, size);
SET_DIM(bp, dim);
s = str1;
i = 0;
if (dim > 0)
{
bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
}
while ((s = strchr(s, ',')) != NULL)
{
s++;
bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
}
Assert(i == dim);
s = str2;
if (dim > 0)
{
bp->x[i] = float8in_internal(s, &endptr, "cube", str2, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
/* code this way to do right thing with NaN */
point &= (bp->x[i] == bp->x[0]);
i++;
}
while ((s = strchr(s, ',')) != NULL)
{
s++;
bp->x[i] = float8in_internal(s, &endptr, "cube", str2, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
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
* 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);
}
*result = bp;
return true;
}
static bool
write_point_as_box(int dim, char *str,
NDBOX **result, struct Node *escontext)
{
NDBOX *bp;
int i,
size;
char *s;
char *endptr;
size = POINT_SIZE(dim);
bp = palloc0(size);
SET_VARSIZE(bp, size);
SET_DIM(bp, dim);
SET_POINT_BIT(bp);
s = str;
i = 0;
if (dim > 0)
{
bp->x[i++] = float8in_internal(s, &endptr, "cube", str, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
}
while ((s = strchr(s, ',')) != NULL)
{
s++;
bp->x[i++] = float8in_internal(s, &endptr, "cube", str, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return false;
}
Assert(i == dim);
*result = bp;
return true;
}