From 7cfdab53eb9b69ec0f80a5a4f9f8b3aeab74b490 Mon Sep 17 00:00:00 2001 From: Dave Gosselin Date: Wed, 11 Jun 2025 13:31:33 -0400 Subject: [PATCH] MDEV-35739 ST_INTERSECTION precise self-intersection ST_INTERSECTION(geom_1, geom_1) returns geom_1 exactly. Replaces 'goto exit;' in Item_func_spatial_operation::val_str with SCOPE_EXIT. This was done to leverage existing geometry construction calls without incurring compiler errors caused by skipping initialization on goto. --- mysql-test/main/gis.result | 21 +++++++++++++++++++++ mysql-test/main/gis.test | 18 ++++++++++++++++++ sql/item_geofunc.cc | 38 ++++++++++++++++++++++++++++---------- sql/spatial.cc | 22 ++++++++++++++++++++++ sql/spatial.h | 2 ++ 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/mysql-test/main/gis.result b/mysql-test/main/gis.result index 6578733cefe..1c8aef3b737 100644 --- a/mysql-test/main/gis.result +++ b/mysql-test/main/gis.result @@ -5507,3 +5507,24 @@ ST_SRID(g1) ST_SRID(ST_GeomFromWKB(g1, 4326)) ST_SRID(ST_GeomFromWKB(g1)) ST_AsT SELECT ST_DISTANCE_SPHERE(st_geomfromtext('linestring( 2 2, 2 8) '), ST_GeomFromText('POINT(18.413076 43.856258)')) ; ERROR HY000: Calling geometry function st_distance_sphere with unsupported types of arguments. # End of 10.5 tests +# +# Start of 10.11 tests +# +# +# MDEV-35739 Linestring self-intersection equality +# +CREATE TABLE t1(geom geometry NOT NULL); +INSERT INTO t1 (geom) VALUES(ST_GeomFromText('LINESTRING(2 2,4 4,6 2,3 2,2 4)')); +SELECT ST_ASTEXT(geom) FROM t1; +ST_ASTEXT(geom) +LINESTRING(2 2,4 4,6 2,3 2,2 4) +SELECT ST_EQUALS(geom, ST_INTERSECTION(geom, geom)) AS isequal, ST_ASTEXT(ST_INTERSECTION(geom, geom)) AS intersection FROM t1; +isequal intersection +1 LINESTRING(2 2,4 4,6 2,3 2,2 4) +SELECT ST_AsText(ST_SYMDIFFERENCE(geom, ST_Intersection(geom, geom))) FROM t1; +ST_AsText(ST_SYMDIFFERENCE(geom, ST_Intersection(geom, geom))) +GEOMETRYCOLLECTION EMPTY +DROP TABLE t1; +# +# End of 10.11 tests +# diff --git a/mysql-test/main/gis.test b/mysql-test/main/gis.test index 0a5734c892e..13473833572 100644 --- a/mysql-test/main/gis.test +++ b/mysql-test/main/gis.test @@ -3515,3 +3515,21 @@ FROM ( SELECT ST_DISTANCE_SPHERE(st_geomfromtext('linestring( 2 2, 2 8) '), ST_GeomFromText('POINT(18.413076 43.856258)')) ; --echo # End of 10.5 tests + +--echo # +--echo # Start of 10.11 tests +--echo # + +--echo # +--echo # MDEV-35739 Linestring self-intersection equality +--echo # +CREATE TABLE t1(geom geometry NOT NULL); +INSERT INTO t1 (geom) VALUES(ST_GeomFromText('LINESTRING(2 2,4 4,6 2,3 2,2 4)')); +SELECT ST_ASTEXT(geom) FROM t1; +SELECT ST_EQUALS(geom, ST_INTERSECTION(geom, geom)) AS isequal, ST_ASTEXT(ST_INTERSECTION(geom, geom)) AS intersection FROM t1; +SELECT ST_AsText(ST_SYMDIFFERENCE(geom, ST_Intersection(geom, geom))) FROM t1; +DROP TABLE t1; + +--echo # +--echo # End of 10.11 tests +--echo # diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index af2b8dfd261..eb7097f54c6 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1547,6 +1547,12 @@ String *Item_func_spatial_operation::val_str(String *str_value) { DBUG_ENTER("Item_func_spatial_operation::val_str"); DBUG_ASSERT(fixed()); + SCOPE_EXIT([this] () { + collector.reset(); + func.reset(); + res_receiver.reset(); + }); + Geometry_ptr_with_buffer_and_mbr g1, g2; uint32 srid= 0; Gcalc_operation_transporter trn(&func, &collector); @@ -1559,7 +1565,23 @@ String *Item_func_spatial_operation::val_str(String *str_value) g2.construct(args[1], &tmp_value2)))) { str_value= 0; - goto exit; + DBUG_RETURN(str_value); + } + + /* + Optimization to evaluate the particular self-intersection + ST_INTERSECTION(geom, geom); + */ + if (spatial_op == Gcalc_function::op_type::op_intersection && + *g1.geom == *g2.geom) + { + /* + Return either argument as the result, it doesn't matter which because + they're identical. + */ + String *sres= args[0]->val_str(&tmp_value1); // tmp_value1 is empty... + str_value->swap(*sres); // ...so swap the val_str result with str_value... + DBUG_RETURN(str_value); // ...to create a valid query result. } g1.mbr.add_mbr(&g2.mbr); @@ -1568,33 +1590,29 @@ String *Item_func_spatial_operation::val_str(String *str_value) if ((null_value= g1.store_shapes(&trn) || g2.store_shapes(&trn))) { str_value= 0; - goto exit; + DBUG_RETURN(str_value); } collector.prepare_operation(); if (func.alloc_states()) - goto exit; + DBUG_RETURN(str_value); operation.init(&func); if (operation.count_all(&collector) || operation.get_result(&res_receiver)) - goto exit; + DBUG_RETURN(str_value); str_value->set_charset(&my_charset_bin); str_value->length(0); if (str_value->reserve(SRID_SIZE, 512)) - goto exit; + DBUG_RETURN(str_value); str_value->q_append(srid); if (!Geometry::create_from_opresult(&g1.buffer, str_value, res_receiver)) - goto exit; + DBUG_RETURN(str_value); -exit: - collector.reset(); - func.reset(); - res_receiver.reset(); DBUG_RETURN(str_value); } diff --git a/sql/spatial.cc b/sql/spatial.cc index 9bf6110c991..bfed445e53b 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -424,6 +424,28 @@ int Geometry::bbox_as_json(String *wkt) } +/** + * Geometry::operator== + * + * @param rhs a Geometry instance to compare to *this + * @return true when *this is the same Geometry as rhs, false otherwise. If + * either *this or rhs have no Geometry associated (because, for + * example, they are built with the default constructor) then the + * comparison will always be false. + */ +bool Geometry::operator==(Geometry &rhs) const +{ + if (!m_data || !rhs.m_data) + return false; + + const ptrdiff_t len= m_data_end - m_data; + if (len != rhs.m_data_end - rhs.m_data) + return false; + + return memcmp(m_data, rhs.m_data, len) == 0; +} + + static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo) { double res; diff --git a/sql/spatial.h b/sql/spatial.h index d775e14aea4..71e08a59ce2 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -384,6 +384,8 @@ public: (expected_points > ((m_data_end - data) / (POINT_DATA_SIZE + extra_point_space)))); } + + bool operator==(Geometry &rhs) const; protected: const char *m_data; const char *m_data_end;