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.
This commit is contained in:
Dave Gosselin 2025-06-11 13:31:33 -04:00
parent c427618462
commit 7cfdab53eb
5 changed files with 91 additions and 10 deletions

View File

@ -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
#

View File

@ -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 #

View File

@ -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);
}

View File

@ -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;

View File

@ -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;