Skip to content

Commit c50c662

Browse files
[PART-1] Figure and Shape Parser support (#4681)
Fix geography validation bugs, replace ad-hoc CLR binary metadata parsing with spec-compliant implementation, and fix polygon Valid flag in properties byte. **Currently**, Babelfish has the following issues in `spatialtypes.c`: 1. **Geography accepts out-of-range longitude values.** `geography::Point(45, 20000, 4326)` succeeds silently. T-SQL enforces longitude bounds of −15069 to +15069 degrees and rejects this input. 2. **Geometry/geography accepts Infinity coordinates.** Binary data containing `±Infinity` coordinate values passes all validation because only `isnan()` is checked, not `isinf()`. T-SQL rejects both NaN and Infinity. 3. **CLR binary metadata (Figure/Shape arrays) is parsed incorrectly.** The old `check_geom_end_metadata()` reads 4 bytes at a time across 5-byte figure entry boundaries. It works only because the attribute byte happens to produce the expected uint32 value when the following offset bytes are zeros. No bounds checking exists on metadata reads. 4. **Polygon properties byte missing Valid flag.** Babelfish outputs `0x00` for valid 2D polygons while T-SQL outputs `0x04` (V flag set). Linestrings already handled valid/invalid variants correctly. 5. **`set_dimension_flag()` prematurely guesses geometry type.** Properties byte `0x04` was hardcoded as `LINE_TYPE`, creating a latent bug where valid polygons from T-SQL would be misidentified. **With this change:** 1. New `validate_longitude_range()` and `validate_not_inf_nan()` functions added and integrated into all validation paths (text input, binary input, direct point creation). Geography objects with longitude outside ±15069° are now rejected. 2. `check_nan_coordinates()` now checks both `isnan()` and `isinf()`. All geography validation paths also check for infinity. 3. New `parse_figures_and_shapes()` replaces `check_geom_end_metadata()`. Reads figure entries as 1+4 bytes and shape entries as 4+4+1 bytes per the T-SQL CLR spec (sections 2.1.3–2.1.4). Includes `CHECK_METADATA_BOUNDS` macro for buffer safety. Geometry type determined from Shape type byte — the single authoritative source. 4. Added `VALID_POLYGON_2D` through `VALID_POLYGON_3DM` constants. `determine_geom_dimensions()` now uses `is_valid` for polygons, matching the existing linestring pattern. 5. `set_dimension_flag()` no longer guesses `LINE_TYPE` for `0x04`. Falls through to `COMPLEX_GEOM_2D`, letting `parse_figures_and_shapes()` determine the actual type from Shape metadata. 6. `geography_from_bytea()` returns NULL for SRID = −1 input instead of crashing on length validation. ### Issues Resolved - Geography silently accepts longitude values outside ±15069° range - Geometry/geography silently accepts ±Infinity coordinates - CLR Figure metadata parsed by reading 4 bytes across 5-byte entry boundaries (works by coincidence) - CLR Shape metadata not parsed at all (geometry type guessed from byte patterns) - Polygon properties byte missing Valid flag (byte 5 mismatch with T-SQL) - `set_dimension_flag()` hardcodes `LINE_TYPE` for properties byte `0x04` (latent bug for valid polygons) - NULL geography (SRID = −1) causes crash instead of returning NULL ### Test Scenarios Covered ### * **Use case based -** - Point: 2D, 3DM, Empty (geometry) - LineString: 2-point, multi-point, Empty (geometry) - Polygon: invalid 2D, invalid 3D, invalid multi-ring, valid 2D, valid 3D, valid 3DM, self-intersecting, Empty (geometry) - Geography: Point, LineString, single-ring Polygon - Round-trip: geometry → varbinary → geometry for polygon * **Boundary conditions -** - `geography::Point(45, 15069, 4326)` — longitude at positive boundary, must succeed - `geography::Point(45, -15069, 4326)` — longitude at negative boundary, must succeed - `geography::Point(90, 0, 4326)` — latitude at boundary, must succeed * **Arbitrary inputs -** - 3-ring polygon (exterior + 2 interior rings) - Valid polygon (proper square) to verify V flag in properties byte - Self-intersecting polygon (invalid geometry) * **Negative test cases -** - `geography::Point(45, 20000, 4326)` — longitude out of range, must error - `geography::Point(45, -20000, 4326)` — negative longitude out of range, must error - `geography::STGeomFromText('LINESTRING(0 0, 20000 45, 1 1)', 4326)` — longitude in linestring, must error - `geography::Point(91, 0, 4326)` — latitude out of range, must error - `CAST(0x00000000010C000000000000F07F0000000000004940 AS geometry)` — Infinity coordinate, must error - `CAST(0x00000000010C000000000000F87F0000000000004940 AS geometry)` — NaN coordinate, must error - `CAST(0xFFFFFFFF AS geography)` — SRID = −1, must return NULL * **Minor version upgrade tests -** N/A — no catalog or protocol changes * **Major version upgrade tests -** N/A — no catalog or protocol changes * **Performance tests -** N/A — validation adds negligible overhead (one extra isinf() call per coordinate, one memcpy for longitude extraction) * **Tooling impact -** N/A — no changes to tooling * **Client tests -** N/A — no client protocol changes Task: BABEL-6377 Signed-off-by: Gopal Verma <gopalgv@amazon.com>
1 parent 193c03d commit c50c662

23 files changed

+16856
-4816
lines changed

contrib/babelfishpg_common/src/spatialtypes.c

Lines changed: 886 additions & 456 deletions
Large diffs are not rendered by default.

test/JDBC/expected/Test-Linestring-vu-verify.out

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,14 @@ SELECT CAST(0xE6100000010403000000000000000000F03F000000000000084000000000000014
217217
GO
218218
~~ERROR (Code: 33557097)~~
219219

220-
~~ERROR (Message: Unsupported geometry type)~~
220+
~~ERROR (Message: Error converting data type varbinary.)~~
221221

222222

223223
SELECT CAST(0xE6100000010403000000000000000000F03F000000000000084000000000000014400000000000001C40000000000000104000000000000001000000010000000001000000FFFFFFFF0000000002 as geography)
224224
GO
225225
~~ERROR (Code: 33557097)~~
226226

227-
~~ERROR (Message: Unsupported geometry type)~~
227+
~~ERROR (Message: Error converting data type varbinary.)~~
228228

229229

230230
-- Test with Invalid bytes
@@ -237,9 +237,10 @@ GO
237237

238238
SELECT CAST(0xE6100000010903000000000000000000F03F000000000000084000000000000014400000000000001C400000000000001040000000000000144001000000010000000001000000FFFFFFFF0000000002 as geometry)
239239
GO
240-
~~ERROR (Code: 33557097)~~
241-
242-
~~ERROR (Message: Unsupported geometry type)~~
240+
~~START~~
241+
geometry
242+
E6100000010D03000000000000000000F03F000000000000084000000000
243+
~~END~~
243244

244245

245246
SELECT CAST(0xE610000001180000000000000840000000000000144000000000000000400000000000001040 as geography)
@@ -251,9 +252,10 @@ GO
251252

252253
SELECT CAST(0xE6100000010903000000000000000000F03F000000000000084000000000000014400000000000001C400000000000001040000000000000144001000000010000000001000000FFFFFFFF0000000002 as geography)
253254
GO
254-
~~ERROR (Code: 33557097)~~
255-
256-
~~ERROR (Message: Unsupported geometry type)~~
255+
~~START~~
256+
geography
257+
E6100000010D03000000000000000000F03F000000000000084000000000
258+
~~END~~
257259

258260

259261
-- When SRID is above 999999 for geometry
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
2+
3+
DROP TABLE geometryAsVarcharpolygon
4+
5+
DROP TABLE geographyAsVarcharpolygon
6+
7+
DROP TABLE CharAsgeometrypolygon
8+
9+
DROP TABLE CharAsgeographypolygon
10+
11+
DROP TABLE VarCharAsgeometrypolygon
12+
13+
DROP TABLE VarcharAsgeographypolygon
14+
15+
DROP TABLE GeoVarCharAsgeometrypolygon
16+
17+
DROP TABLE GeoVarcharAsgeographypolygon
18+
19+
DROP VIEW DimOfGeometrypolygon
20+
21+
DROP VIEW AreaOfGeometrypolygon
22+
23+
DROP VIEW TextFromGeometrypolygon
24+
25+
DROP VIEW BinaryFromGeometrypolygon
26+
27+
DROP VIEW SridFromGeometrypolygon
28+
29+
DROP VIEW EmptyGeometrypolygon
30+
31+
DROP VIEW ValidGeometrypolygon
32+
33+
DROP VIEW ClosedGeometrypolygon
34+
35+
DROP VIEW DisjointTempGeompolygon
36+
37+
DROP VIEW DistanceTempGeompolygon
38+
39+
DROP VIEW IntersectsTempGeompolygon
40+
41+
DROP VIEW ContainTempGeompolygon
42+
43+
DROP VIEW EqualsTempGeompolygon
44+
45+
DROP VIEW equals_opgeompolygon
46+
47+
DROP VIEW notequal_opgeompolygon
48+
49+
DROP VIEW DisjointTempGeompolysr
50+
51+
DROP VIEW DistanceTempGeompolysr
52+
53+
DROP VIEW IntersectsTempGeompolysr
54+
55+
DROP VIEW EqualsTempGeompolysr
56+
57+
DROP VIEW ContainTempGeompolysr
58+
59+
DROP VIEW equals_opgeompolysr
60+
61+
DROP VIEW notequal_opgeompolysr
62+
63+
DROP TABLE geometryToByteapolygon
64+
65+
DROP TABLE geometryAsVarbinarypolygon
66+
67+
DROP TABLE CharAsVarbinaryGeompolygon
68+
69+
DROP TABLE VarbinaryAsgeometrypolygon
70+
71+
72+
DROP VIEW DimOfgeographypolygon
73+
74+
DROP VIEW AreaOfgeographypolygon
75+
76+
DROP VIEW TextFromgeographypolygon
77+
78+
DROP VIEW BinaryFromgeographypolygon
79+
80+
DROP VIEW SridFromgeographypolygon
81+
82+
DROP VIEW Emptygeographypolygon
83+
84+
DROP VIEW Validgeographypolygon
85+
86+
DROP VIEW Closedgeographypolygon
87+
88+
DROP VIEW DisjointTempGeogpolygon
89+
90+
DROP VIEW DistanceTempGeogpolygon
91+
92+
DROP VIEW IntersectsTempGeogpolygon
93+
94+
DROP VIEW ContainTempGeogpolygon
95+
96+
DROP VIEW EqualsTempGeogpolygon
97+
98+
DROP VIEW DisjointTempGeogpolysr
99+
100+
DROP VIEW DistanceTempGeogpolysr
101+
102+
DROP VIEW IntersectsTempGeogpolysr
103+
104+
DROP VIEW EqualsTempGeogpolysr
105+
106+
DROP VIEW ContainTempGeogpolysr
107+
108+
DROP VIEW equals_opgeogpolygon
109+
110+
DROP VIEW notequal_opgeogpolygon
111+
112+
DROP VIEW equals_opgeogpolysr
113+
114+
DROP VIEW notequal_opgeogpolysr
115+
116+
DROP VIEW PolygonGeogFromText
117+
118+
DROP VIEW PolygonGeomFromText
119+
120+
DROP TABLE geographyToByteapolygon
121+
122+
DROP TABLE geographyAsVarbinarypolygon
123+
124+
DROP TABLE CharAsVarbinaryGeogpolygon
125+
126+
DROP TABLE VarbinaryAsgeographypolygon
127+
128+
129+
DROP TABLE geospatialpointgeom_polysr
130+
131+
DROP TABLE GEOSPATIALPOLYGONGEOM_dt
132+
133+
DROP TABLE GEOSPATIALPOLYGONGEOM_INVALID_dt
134+
135+
DROP TABLE GEOSPATIALPOLYGONGEOG_INVALID_dt
136+
137+
DROP TABLE geospatialpointxgeog_polysr
138+
139+
DROP TABLE GEOSPATIALPOLYGONGEOG_dt
140+
141+
DROP TABLE GEOSPATIAL_POLYGEOMRCV_dt
142+
143+
DROP TABLE GEOSPATIAL_POLYGEOGRCV_dt

0 commit comments

Comments
 (0)