1 %{
2 /* contrib/cube/cubeparse.y */
3 
4 /* NdBox = [(lowerleft),(upperright)] */
5 /* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
6 
7 #include "postgres.h"
8 
9 #include "cubedata.h"
10 #include "utils/float.h"
11 
12 /* All grammar constructs return strings */
13 #define YYSTYPE char *
14 
15 /*
16  * Bison doesn't allocate anything that needs to live across parser calls,
17  * so we can easily have it use palloc instead of malloc.  This prevents
18  * memory leaks if we error out during parsing.  Note this only works with
19  * bison >= 2.0.  However, in bison 1.875 the default is to use alloca()
20  * if possible, so there's not really much problem anyhow, at least if
21  * you're building with gcc.
22  */
23 #define YYMALLOC palloc
24 #define YYFREE   pfree
25 
26 static char *scanbuf;
27 static int	scanbuflen;
28 
29 static int item_count(const char *s, char delim);
30 static NDBOX *write_box(int dim, char *str1, char *str2);
31 static NDBOX *write_point_as_box(int dim, char *str);
32 
33 %}
34 
35 /* BISON Declarations */
36 %parse-param {NDBOX **result}
37 %expect 0
38 %name-prefix="cube_yy"
39 
40 %token CUBEFLOAT O_PAREN C_PAREN O_BRACKET C_BRACKET COMMA
41 %start box
42 
43 /* Grammar follows */
44 %%
45 
46 box: O_BRACKET paren_list COMMA paren_list C_BRACKET
47 	{
48 		int dim;
49 
50 		dim = item_count($2, ',');
51 		if (item_count($4, ',') != dim)
52 		{
53 			ereport(ERROR,
54 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
55 					 errmsg("invalid input syntax for cube"),
56 					 errdetail("Different point dimensions in (%s) and (%s).",
57 							   $2, $4)));
58 			YYABORT;
59 		}
60 		if (dim > CUBE_MAX_DIM)
61 		{
62 			ereport(ERROR,
63 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
64 					 errmsg("invalid input syntax for cube"),
65 					 errdetail("A cube cannot have more than %d dimensions.",
66 							   CUBE_MAX_DIM)));
67 			YYABORT;
68 		}
69 
70 		*result = write_box( dim, $2, $4 );
71 	}
72 
73 	| paren_list COMMA paren_list
74 	{
75 		int dim;
76 
77 		dim = item_count($1, ',');
78 		if (item_count($3, ',') != dim)
79 		{
80 			ereport(ERROR,
81 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
82 					 errmsg("invalid input syntax for cube"),
83 					 errdetail("Different point dimensions in (%s) and (%s).",
84 							   $1, $3)));
85 			YYABORT;
86 		}
87 		if (dim > CUBE_MAX_DIM)
88 		{
89 			ereport(ERROR,
90 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
91 					 errmsg("invalid input syntax for cube"),
92 					 errdetail("A cube cannot have more than %d dimensions.",
93 							   CUBE_MAX_DIM)));
94 			YYABORT;
95 		}
96 
97 		*result = write_box( dim, $1, $3 );
98 	}
99 
100 	| paren_list
101 	{
102 		int dim;
103 
104 		dim = item_count($1, ',');
105 		if (dim > CUBE_MAX_DIM)
106 		{
107 			ereport(ERROR,
108 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
109 					 errmsg("invalid input syntax for cube"),
110 					 errdetail("A cube cannot have more than %d dimensions.",
111 							   CUBE_MAX_DIM)));
112 			YYABORT;
113 		}
114 
115 		*result = write_point_as_box(dim, $1);
116 	}
117 
118 	| list
119 	{
120 		int dim;
121 
122 		dim = item_count($1, ',');
123 		if (dim > CUBE_MAX_DIM)
124 		{
125 			ereport(ERROR,
126 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
127 					 errmsg("invalid input syntax for cube"),
128 					 errdetail("A cube cannot have more than %d dimensions.",
129 							   CUBE_MAX_DIM)));
130 			YYABORT;
131 		}
132 
133 		*result = write_point_as_box(dim, $1);
134 	}
135 	;
136 
137 paren_list: O_PAREN list C_PAREN
138 	{
139 		$$ = $2;
140 	}
141 	| O_PAREN C_PAREN
142 	{
143 		$$ = pstrdup("");
144 	}
145 	;
146 
147 list: CUBEFLOAT
148 	{
149 		/* alloc enough space to be sure whole list will fit */
150 		$$ = palloc(scanbuflen + 1);
151 		strcpy($$, $1);
152 	}
153 	| list COMMA CUBEFLOAT
154 	{
155 		$$ = $1;
156 		strcat($$, ",");
157 		strcat($$, $3);
158 	}
159 	;
160 
161 %%
162 
163 /* This assumes the string has been normalized by productions above */
164 static int
165 item_count(const char *s, char delim)
166 {
167 	int			nitems = 0;
168 
169 	if (s[0] != '\0')
170 	{
171 		nitems++;
172 		while ((s = strchr(s, delim)) != NULL)
173 		{
174 			nitems++;
175 			s++;
176 		}
177 	}
178 	return nitems;
179 }
180 
181 static NDBOX *
write_box(int dim,char * str1,char * str2)182 write_box(int dim, char *str1, char *str2)
183 {
184 	NDBOX	   *bp;
185 	char	   *s;
186 	char	   *endptr;
187 	int			i;
188 	int			size = CUBE_SIZE(dim);
189 	bool		point = true;
190 
191 	bp = palloc0(size);
192 	SET_VARSIZE(bp, size);
193 	SET_DIM(bp, dim);
194 
195 	s = str1;
196 	i = 0;
197 	if (dim > 0)
198 		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
199 	while ((s = strchr(s, ',')) != NULL)
200 	{
201 		s++;
202 		bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
203 	}
204 	Assert(i == dim);
205 
206 	s = str2;
207 	if (dim > 0)
208 	{
209 		bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
210 		/* code this way to do right thing with NaN */
211 		point &= (bp->x[i] == bp->x[0]);
212 		i++;
213 	}
214 	while ((s = strchr(s, ',')) != NULL)
215 	{
216 		s++;
217 		bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
218 		point &= (bp->x[i] == bp->x[i - dim]);
219 		i++;
220 	}
221 	Assert(i == dim * 2);
222 
223 	if (point)
224 	{
225 		/*
226 		 * The value turned out to be a point, ie. all the upper-right
227 		 * coordinates were equal to the lower-left coordinates. Resize the
228 		 * cube we constructed.  Note: we don't bother to repalloc() it
229 		 * smaller, as it's unlikely that the tiny amount of memory freed
230 		 * that way would be useful, and the output is always short-lived.
231 		 */
232 		size = POINT_SIZE(dim);
233 		SET_VARSIZE(bp, size);
234 		SET_POINT_BIT(bp);
235 	}
236 
237 	return bp;
238 }
239 
240 static NDBOX *
write_point_as_box(int dim,char * str)241 write_point_as_box(int dim, char *str)
242 {
243 	NDBOX		*bp;
244 	int			i,
245 				size;
246 	char	   *s;
247 	char	   *endptr;
248 
249 	size = POINT_SIZE(dim);
250 	bp = palloc0(size);
251 	SET_VARSIZE(bp, size);
252 	SET_DIM(bp, dim);
253 	SET_POINT_BIT(bp);
254 
255 	s = str;
256 	i = 0;
257 	if (dim > 0)
258 		bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
259 	while ((s = strchr(s, ',')) != NULL)
260 	{
261 		s++;
262 		bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
263 	}
264 	Assert(i == dim);
265 
266 	return bp;
267 }
268 
269 #include "cubescan.c"
270