diff --git a/mysql-test/main/gis.result b/mysql-test/main/gis.result index 2a32dcc61a9..14a101f8253 100644 --- a/mysql-test/main/gis.result +++ b/mysql-test/main/gis.result @@ -5569,3 +5569,24 @@ ERROR HY000: Cannot cast 'int' as 'point' in assignment of `test`.`t1`.`b` CREATE TABLE t1 (a POINT, b INT GENERATED ALWAYS AS (a)); ERROR HY000: Cannot cast 'point' as 'int' in assignment of `test`.`t1`.`b` # End of 11.5 tests +# +# Start of 11.8 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 11.8 tests +# diff --git a/mysql-test/main/gis.test b/mysql-test/main/gis.test index abae0af4c3b..0d2aad7a0e5 100644 --- a/mysql-test/main/gis.test +++ b/mysql-test/main/gis.test @@ -3574,3 +3574,21 @@ CREATE TABLE t1 (a INT, b POINT GENERATED ALWAYS AS (a)); CREATE TABLE t1 (a POINT, b INT GENERATED ALWAYS AS (a)); --echo # End of 11.5 tests + +--echo # +--echo # Start of 11.8 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 11.8 tests +--echo # diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 1f1815d8db7..13ce34d283c 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1542,6 +1542,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); @@ -1554,7 +1560,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); @@ -1563,33 +1585,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 0198fdb8055..81bb89c68c8 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -422,6 +422,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 db61cd60ef1..fcaa94c154f 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -382,6 +382,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;