diff --git a/spatial/include/spatial/core/geometry/geometry_processor.hpp b/spatial/include/spatial/core/geometry/geometry_processor.hpp index 3567bcae..c70b763c 100644 --- a/spatial/include/spatial/core/geometry/geometry_processor.hpp +++ b/spatial/include/spatial/core/geometry/geometry_processor.hpp @@ -220,8 +220,13 @@ class GeometryProcessor { public: RESULT Process(const geometry_t &geom, ARGS... args) { - has_z = geom.GetProperties().HasZ(); - has_m = geom.GetProperties().HasM(); + const auto props = geom.GetProperties(); + + // Check the version + props.CheckVersion(); + + has_z = props.HasZ(); + has_m = props.HasM(); nesting_level = 0; current_type = geom.GetType(); parent_type = GeometryType::POINT; diff --git a/spatial/include/spatial/core/geometry/geometry_properties.hpp b/spatial/include/spatial/core/geometry/geometry_properties.hpp index 49e71e9c..a3d042c9 100644 --- a/spatial/include/spatial/core/geometry/geometry_properties.hpp +++ b/spatial/include/spatial/core/geometry/geometry_properties.hpp @@ -5,6 +5,8 @@ namespace spatial { namespace core { +static constexpr const uint8_t GEOMETRY_VERSION = 0; + struct GeometryProperties { private: static constexpr const uint8_t Z = 0x01; @@ -14,6 +16,8 @@ struct GeometryProperties { // static constexpr const uint8_t EMPTY = 0x08; // static constexpr const uint8_t GEODETIC = 0x10; // static constexpr const uint8_t SOLID = 0x20; + static constexpr const uint8_t VERSION_1 = 0x40; + static constexpr const uint8_t VERSION_0 = 0x80; uint8_t flags = 0; public: @@ -24,6 +28,16 @@ struct GeometryProperties { SetM(has_m); } + inline void CheckVersion() const { + const auto v0 = (flags & VERSION_0); + const auto v1 = (flags & VERSION_1); + if ((v1 | v0) != GEOMETRY_VERSION) { + throw NotImplementedException( + "This geometry seems to be written with a newer version of the DuckDB spatial library that is not " + "compatible with this version. Please upgrade your DuckDB installation."); + } + } + inline bool HasZ() const { return (flags & Z) != 0; } diff --git a/spatial/include/spatial/core/geometry/geometry_type.hpp b/spatial/include/spatial/core/geometry/geometry_type.hpp index 58fc0339..2a13c629 100644 --- a/spatial/include/spatial/core/geometry/geometry_type.hpp +++ b/spatial/include/spatial/core/geometry/geometry_type.hpp @@ -83,13 +83,15 @@ class geometry_t { } GeometryType GetType() const { + // return the type return Load(const_data_ptr_cast(data.GetPrefix())); } + GeometryProperties GetProperties() const { - return Load(const_data_ptr_cast(data.GetPrefix() + 1)); - } - uint16_t GetHash() const { - return Load(const_data_ptr_cast(data.GetPrefix() + 2)); + const auto props = Load(const_data_ptr_cast(data.GetPrefix() + 1)); + // Check the version + props.CheckVersion(); + return props; } bool TryGetCachedBounds(Box2D &bbox) const { @@ -101,6 +103,9 @@ class geometry_t { auto hash = cursor.Read(); (void)hash; + // Check the version + properties.CheckVersion(); + if (properties.HasBBox()) { cursor.Skip(4); // skip padding diff --git a/test/data/duckdb_v1_0_0.db b/test/data/duckdb_v1_0_0.db new file mode 100644 index 00000000..268b4702 Binary files /dev/null and b/test/data/duckdb_v1_0_0.db differ diff --git a/test/sql/geometry/geometry_version.test b/test/sql/geometry/geometry_version.test new file mode 100644 index 00000000..0cf2652e --- /dev/null +++ b/test/sql/geometry/geometry_version.test @@ -0,0 +1,27 @@ +# Query a database with geometry types created in duckdb v1.0.0 +require spatial + +statement ok +attach 'test/data/duckdb_v1_0_0.db' as db; + +statement ok +use db + +query IIII rowsort +SELECT st_geometrytype(geom), st_astext(geom), st_isvalid(geom), st_area(geom) FROM types; +---- +GEOMETRYCOLLECTION GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (0 0, 1 1)) true 0.0 +GEOMETRYCOLLECTION GEOMETRYCOLLECTION EMPTY true 0.0 +LINESTRING LINESTRING (0 0, 1 1) true 0.0 +LINESTRING LINESTRING EMPTY true 0.0 +MULTILINESTRING MULTILINESTRING ((0 0, 1 1), (2 2, 3 3)) true 0.0 +MULTILINESTRING MULTILINESTRING EMPTY true 0.0 +MULTIPOINT MULTIPOINT (0 0, 1 1) true 0.0 +MULTIPOINT MULTIPOINT EMPTY true 0.0 +MULTIPOLYGON MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2))) true 2.0 +MULTIPOLYGON MULTIPOLYGON EMPTY true 0.0 +POINT POINT (0 0) true 0.0 +POINT POINT EMPTY true 0.0 +POLYGON POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)) true 1.0 +POLYGON POLYGON EMPTY true 0.0 +