[Commits] d0d6284: MDEV-4045 Missing OGC Spatial functions.

holyfoot at askmonty.org holyfoot at askmonty.org
Thu Nov 27 22:32:30 EET 2014


revision-id: d0d6284cab70af148269d95744f615454cac24bb
parent(s): 7b55b67de5fdfe91283357fe6c7ccc3f9e355925
committer: Alexey Botchkov
branch nick: mdev-7171
timestamp: 2014-11-28 00:29:37 +0400
message:

MDEV-4045 Missing OGC Spatial functions.

   Missing GIS functions added:
        IsRing()
        PointOnSurface
        PointOnSurface
        Relate
        Distance
        Intersection
        ConvexHull
   Other old OpenGis standard inconsistencies fixed.

---
 mysql-test/r/gis.result |  68 +++++
 mysql-test/t/gis.test   | 126 ++++------
 sql/gcalc_slicescan.cc  |   4 +-
 sql/item_create.cc      | 130 +++++++++-
 sql/item_func.h         |   2 +-
 sql/item_geofunc.cc     | 651 +++++++++++++++++++++++++++++++++++++++++++++++-
 sql/item_geofunc.h      |  95 ++++++-
 sql/spatial.cc          |  17 +-
 sql/spatial.h           |   4 +-
 9 files changed, 996 insertions(+), 101 deletions(-)

diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result
index 70e5215..baedf17 100644
--- a/mysql-test/r/gis.result
+++ b/mysql-test/r/gis.result
@@ -1326,6 +1326,18 @@ WHERE name = 'Route 5'
 AND aliases = 'Main Street';
 IsEmpty(centerline)
 0
+# Conformance Item T12 
+SELECT IsSimple(shore) 
+FROM lakes 
+WHERE name = 'Blue Lake';
+IsSimple(shore)
+1
+# Conformance Item T13 
+SELECT AsText(ST_Boundary(boundary))
+FROM named_places 
+WHERE name = 'Goose Island';
+AsText(ST_Boundary(boundary))
+LINESTRING(67 13,67 18,59 18,59 13,67 13)
 # Conformance Item T14 
 SELECT AsText(Envelope(boundary)) 
 FROM named_places 
@@ -1356,6 +1368,17 @@ FROM road_segments
 WHERE fid = 102;
 AsText(EndPoint(centerline))
 POINT(44 31)
+SELECT IsClosed(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
+FROM named_places 
+WHERE name = 'Goose Island';
+IsClosed(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary)))
+1
+# Conformance Item T20 
+SELECT IsRing(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
+FROM named_places 
+WHERE name = 'Goose Island';
+IsRing(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary)))
+0
 # Conformance Item T21 
 SELECT GLength(centerline) 
 FROM road_segments 
@@ -1380,6 +1403,11 @@ FROM named_places
 WHERE name = 'Goose Island';
 AsText(Centroid(boundary))
 POINT(63 15.5)
+SELECT ST_Contains(boundary, PointOnSurface(boundary)) 
+FROM named_places 
+WHERE name = 'Goose Island';
+ST_Contains(boundary, PointOnSurface(boundary))
+1
 # Conformance Item T26 
 SELECT Area(boundary) 
 FROM named_places 
@@ -1434,6 +1462,12 @@ FROM ponds
 WHERE fid = 120;
 AsText(Centroid(shores))
 POINT(25 42)
+# Conformance Item T35 
+SELECT Contains(shores, PointOnSurface(shores)) 
+FROM ponds 
+WHERE fid = 120;
+Contains(shores, PointOnSurface(shores))
+1
 # Conformance Item T36 
 SELECT Area(shores) 
 FROM ponds 
@@ -1462,6 +1496,20 @@ WHERE streams.name = 'Cam Stream'
 AND lakes.name = 'Blue Lake';
 ST_Touches(centerline, shore)
 1
+# Conformance Item T40 
+SELECT ST_Within(footprint, boundary) 
+FROM named_places, buildings 
+WHERE named_places.name = 'Ashton' 
+AND buildings.address = '215 Main Street';
+ST_Within(footprint, boundary)
+1
+# Conformance Item T41 
+SELECT ST_Overlaps(forests.boundary, named_places.boundary) 
+FROM forests, named_places 
+WHERE forests.name = 'Green Forest' 
+AND named_places.name = 'Ashton';
+ST_Overlaps(forests.boundary, named_places.boundary)
+1
 # Conformance Item T42
 SELECT Crosses(road_segments.centerline, divided_routes.centerlines)
 FROM road_segments, divided_routes 
@@ -1483,6 +1531,13 @@ WHERE forests.name = 'Green Forest'
 AND named_places.name = 'Ashton';
 ST_Contains(forests.boundary, named_places.boundary)
 0
+# Conformance Item T45 
+SELECT ST_Relate(forests.boundary, named_places.boundary, 'TTTTTTTTT') 
+FROM forests, named_places 
+WHERE forests.name = 'Green Forest' 
+AND named_places.name = 'Ashton';
+ST_Relate(forests.boundary, named_places.boundary, 'TTTTTTTTT')
+1
 # Conformance Item T46 
 SELECT ST_Distance(position, boundary) 
 FROM bridges, named_places 
@@ -1490,6 +1545,13 @@ WHERE bridges.name = 'Cam Bridge'
 AND named_places.name = 'Ashton';
 ST_Distance(position, boundary)
 12
+# Conformance Item T47 
+SELECT AsText(ST_Intersection(centerline, shore)) 
+FROM streams, lakes 
+WHERE streams.name = 'Cam Stream' 
+AND lakes.name = 'Blue Lake';
+AsText(ST_Intersection(centerline, shore))
+POINT(52 18)
 # Conformance Item T48 
 SELECT AsText(ST_Difference(named_places.boundary, forests.boundary)) 
 FROM named_places, forests 
@@ -1516,6 +1578,12 @@ FROM buildings, bridges
 WHERE ST_Contains(ST_Buffer(bridges.position, 15.0), buildings.footprint) = 1;
 count(*)
 1
+# Conformance Item T52 
+SELECT AsText(ConvexHull(shore)) 
+FROM lakes 
+WHERE lakes.name = 'Blue Lake';
+AsText(ConvexHull(shore))
+POLYGON((48 6,52 18,66 23,73 9,48 6))
 DROP DATABASE gis_ogs;
 USE test;
 #
diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test
index 5dc5b48..60642e5 100644
--- a/mysql-test/t/gis.test
+++ b/mysql-test/t/gis.test
@@ -1119,33 +1119,27 @@ FROM named_places
 WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T10 
-# TODO: ST_SRID() alias
 SELECT SRID(boundary) 
 FROM named_places 
 WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T11 
-# TODO: ST_IsEmpty() alias
 SELECT IsEmpty(centerline) 
 FROM road_segments 
 WHERE name = 'Route 5' 
 AND aliases = 'Main Street'; 
 
-# FIXME: get wrong result:0, expected 1.
-#--echo # Conformance Item T12 
-# TODO: ST_IsSimple() alias
-#SELECT IsSimple(shore) 
-#FROM lakes 
-#WHERE name = 'Blue Lake'; 
+--echo # Conformance Item T12 
+SELECT IsSimple(shore) 
+FROM lakes 
+WHERE name = 'Blue Lake'; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T13 
-#SELECT AsText(Boundary((boundary),101) 
-#FROM named_places 
-#WHERE name = 'Goose Island'; 
+--echo # Conformance Item T13 
+SELECT AsText(ST_Boundary(boundary))
+FROM named_places 
+WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T14 
-# TODO: ST_Envelope( ) alias
 # FIXME: we get anticlockwise, GIS suggests clockwise
 SELECT AsText(Envelope(boundary)) 
 FROM named_places 
@@ -1170,122 +1164,100 @@ FROM road_segments
 WHERE fid = 102; 
 
 --echo # Conformance Item T18 
-# TODO: ST_EndPoint
 SELECT AsText(EndPoint(centerline)) 
 FROM road_segments 
 WHERE fid = 102; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T19 
-# TODO: ST_LineFromWKB() alias
-#SELECT IsClosed(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
-#FROM named_places 
-#WHERE name = 'Goose Island'; 
+SELECT IsClosed(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
+FROM named_places 
+WHERE name = 'Goose Island'; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T20 
-#SELECT IsRing(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
-#FROM named_places 
-#WHERE name = 'Goose Island'; 
+--echo # Conformance Item T20 
+SELECT IsRing(LineFromWKB(AsBinary(Boundary(boundary)),SRID(boundary))) 
+FROM named_places 
+WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T21 
-# TODO: ST_Length() alias
 SELECT GLength(centerline) 
 FROM road_segments 
 WHERE fid = 106; 
 
 --echo # Conformance Item T22 
-# TODO: ST_NumPoints() alias
 SELECT NumPoints(centerline) 
 FROM road_segments 
 WHERE fid = 102; 
 
 --echo # Conformance Item T23 
-# TODO: ST_PointN() alias
 SELECT AsText(PointN(centerline, 1)) 
 FROM road_segments 
 WHERE fid = 102; 
 
 --echo # Conformance Item T24 
-# TODO: ST_Centroid() alias
 SELECT AsText(Centroid(boundary)) 
 FROM named_places 
 WHERE name = 'Goose Island'; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T25 
-#SELECT Contains(boundary, PointOnSurface(boundary)) 
-#FROM named_places 
-#WHERE name = 'Goose Island'; 
+SELECT ST_Contains(boundary, PointOnSurface(boundary)) 
+FROM named_places 
+WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T26 
-# TODO: ST_Area() alias
 SELECT Area(boundary) 
 FROM named_places 
 WHERE name = 'Goose Island'; 
 
 --echo # Conformance Item T27 
-# TODO: ST_ExteriorRing() alias
 SELECT AsText(ExteriorRing(shore)) 
 FROM lakes 
 WHERE name = 'Blue Lake'; 
 
 --echo # Conformance Item T28 
-# TODO: ST_NumInteriorRings() alias
 SELECT NumInteriorRings(shore) 
 FROM lakes 
 WHERE name = 'Blue Lake'; 
 
 --echo # Conformance Item T29 
-# TODO: ST_InteriorRingN() alias
 SELECT AsText(InteriorRingN(shore, 1)) 
 FROM lakes 
 WHERE name = 'Blue Lake'; 
 
 --echo # Conformance Item T30 
-# TODO: ST_NumGeometries() alias
 SELECT NumGeometries(centerlines) 
 FROM divided_routes 
 WHERE name = 'Route 75'; 
 
 --echo # Conformance Item T31 
-# TODO: ST_GeometryN() alias
 SELECT AsText(GeometryN(centerlines, 2)) 
 FROM divided_routes 
 WHERE name = 'Route 75'; 
 
 --echo # Conformance Item T32 
-# TODO: ST_IsClosed() alias
 SELECT IsClosed(centerlines) 
 FROM divided_routes 
 WHERE name = 'Route 75'; 
 
 --echo # Conformance Item T33 
-# TODO: ST_Length() alias
 SELECT GLength(centerlines) 
 FROM divided_routes 
 WHERE name = 'Route 75'; 
 
 --echo # Conformance Item T34 
-# TODO: ST_Centroid() alias
 SELECT AsText(Centroid(shores)) 
 FROM ponds 
 WHERE fid = 120; 
 
 # TODO: WL#2377
-#--echo # Conformance Item T35 
-#SELECT Contains(shores, PointOnSurface(shores)) 
-#FROM ponds 
-#WHERE fid = 120; 
+--echo # Conformance Item T35 
+SELECT Contains(shores, PointOnSurface(shores)) 
+FROM ponds 
+WHERE fid = 120; 
 
 --echo # Conformance Item T36 
-# TODO: ST_Area() alias
 SELECT Area(shores) 
 FROM ponds 
 WHERE fid = 120; 
 
 --echo # Conformance Item T37 
-# TODO: ST_PolyFromText() alias
 SELECT ST_Equals(boundary, 
 PolyFromText('POLYGON( ( 67 13, 67 18, 59 18, 59 13, 67 13) )',1)) 
 FROM named_places 
@@ -1303,22 +1275,19 @@ FROM streams, lakes
 WHERE streams.name = 'Cam Stream' 
 AND lakes.name = 'Blue Lake'; 
 
-# FIXME: wrong result: get 0, expected 1
-#--echo # Conformance Item T40 
-#SELECT ST_Within(boundary, footprint) 
-#FROM named_places, buildings 
-#WHERE named_places.name = 'Ashton' 
-#AND buildings.address = '215 Main Street'; 
+--echo # Conformance Item T40 
+SELECT ST_Within(footprint, boundary) 
+FROM named_places, buildings 
+WHERE named_places.name = 'Ashton' 
+AND buildings.address = '215 Main Street'; 
 
-# FIXME: wrong result: get 0, expected 1
-#--echo # Conformance Item T41 
-#SELECT ST_Overlaps(forests.boundary, named_places.boundary) 
-#FROM forests, named_places 
-#WHERE forests.name = 'Green Forest' 
-#AND named_places.name = 'Ashton'; 
+--echo # Conformance Item T41 
+SELECT ST_Overlaps(forests.boundary, named_places.boundary) 
+FROM forests, named_places 
+WHERE forests.name = 'Green Forest' 
+AND named_places.name = 'Ashton'; 
 
 --echo # Conformance Item T42
-# FIXME: TODO: ST_Crosses() alias
 SELECT Crosses(road_segments.centerline, divided_routes.centerlines)
 FROM road_segments, divided_routes 
 WHERE road_segments.fid = 102 
@@ -1336,12 +1305,11 @@ FROM forests, named_places
 WHERE forests.name = 'Green Forest' 
 AND named_places.name = 'Ashton'; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T45 
-#SELECT Relate(forests.boundary, named_places.boundary, 'TTTTTTTTT') 
-#FROM forests, named_places 
-#WHERE forests.name = 'Green Forest' 
-#AND named_places.name = 'Ashton'; 
+--echo # Conformance Item T45 
+SELECT ST_Relate(forests.boundary, named_places.boundary, 'TTTTTTTTT') 
+FROM forests, named_places 
+WHERE forests.name = 'Green Forest' 
+AND named_places.name = 'Ashton'; 
 
 --echo # Conformance Item T46 
 SELECT ST_Distance(position, boundary) 
@@ -1349,12 +1317,11 @@ FROM bridges, named_places
 WHERE bridges.name = 'Cam Bridge' 
 AND named_places.name = 'Ashton'; 
 
-# FIXME: wrong result: NULL, expected 12
-#--echo # Conformance Item T47 
-#SELECT AsText(ST_Intersection(centerline, shore)) 
-#FROM streams, lakes 
-#WHERE streams.name = 'Cam Stream' 
-#AND lakes.name = 'Blue Lake'; 
+--echo # Conformance Item T47 
+SELECT AsText(ST_Intersection(centerline, shore)) 
+FROM streams, lakes 
+WHERE streams.name = 'Cam Stream' 
+AND lakes.name = 'Blue Lake'; 
 
 --echo # Conformance Item T48 
 SELECT AsText(ST_Difference(named_places.boundary, forests.boundary)) 
@@ -1379,11 +1346,10 @@ SELECT count(*)
 FROM buildings, bridges 
 WHERE ST_Contains(ST_Buffer(bridges.position, 15.0), buildings.footprint) = 1; 
 
-# TODO: WL#2377
-#--echo # Conformance Item T52 
-#SELECT AsText(ConvexHull(shore)) 
-#FROM lakes 
-#WHERE lakes.name = 'Blue Lake'; 
+--echo # Conformance Item T52 
+SELECT AsText(ConvexHull(shore)) 
+FROM lakes 
+WHERE lakes.name = 'Blue Lake'; 
 
 DROP DATABASE gis_ogs;
 USE test;
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index 251869c..ed533ab 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -1961,7 +1961,7 @@ double Gcalc_scan_iterator::get_h() const
     state.pi->calc_xy(&x, &next_y);
   }
   else
-    next_y= state.pi->y;
+    next_y= state.pi->next ? state.pi->get_next()->y : 0.0;
   return next_y - cur_y;
 }
 
@@ -1974,7 +1974,7 @@ double Gcalc_scan_iterator::get_sp_x(const point *sp) const
   dy= sp->next_pi->y - sp->pi->y;
   if (fabs(dy) < 1e-12)
     return sp->pi->x;
-  return (sp->next_pi->x - sp->pi->x) * dy;
+  return sp->pi->x + (sp->next_pi->x - sp->pi->x) * dy;
 }
 
 
diff --git a/sql/item_create.cc b/sql/item_create.cc
index fa8249c..efc1d9d 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -513,7 +513,35 @@ class Create_func_centroid : public Create_func_arg1
   Create_func_centroid() {}
   virtual ~Create_func_centroid() {}
 };
-#endif
+
+
+class Create_func_convexhull : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_convexhull s_singleton;
+
+protected:
+  Create_func_convexhull() {}
+  virtual ~Create_func_convexhull() {}
+};
+
+
+class Create_func_pointonsurface : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_pointonsurface s_singleton;
+
+protected:
+  Create_func_pointonsurface() {}
+  virtual ~Create_func_pointonsurface() {}
+};
+
+
+#endif /*HAVE_SPATIAL*/
 
 
 class Create_func_char_length : public Create_func_arg1
@@ -1015,7 +1043,19 @@ class Create_func_envelope : public Create_func_arg1
   Create_func_envelope() {}
   virtual ~Create_func_envelope() {}
 };
-#endif
+
+class Create_func_boundary : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_boundary s_singleton;
+
+protected:
+  Create_func_boundary() {}
+  virtual ~Create_func_boundary() {}
+};
+#endif /*HAVE_SPATIAL*/
 
 
 #ifdef HAVE_SPATIAL
@@ -1466,6 +1506,19 @@ class Create_func_interiorringn : public Create_func_arg2
 
 
 #ifdef HAVE_SPATIAL
+class Create_func_relate : public Create_func_arg3
+{
+public:
+  virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+  static Create_func_relate s_singleton;
+
+protected:
+  Create_func_relate() {}
+  virtual ~Create_func_relate() {}
+};
+
+
 class Create_func_mbr_intersects : public Create_func_arg2
 {
   public:
@@ -1596,6 +1649,19 @@ class Create_func_isclosed : public Create_func_arg1
   Create_func_isclosed() {}
   virtual ~Create_func_isclosed() {}
 };
+
+
+class Create_func_isring : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_isring s_singleton;
+
+protected:
+  Create_func_isring() {}
+  virtual ~Create_func_isring() {}
+};
 #endif
 
 
@@ -3338,7 +3404,25 @@ Create_func_centroid::create_1_arg(THD *thd, Item *arg1)
 {
   return new (thd->mem_root) Item_func_centroid(arg1);
 }
-#endif
+
+
+Create_func_convexhull Create_func_convexhull::s_singleton;
+
+Item*
+Create_func_convexhull::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_convexhull(arg1);
+}
+
+
+Create_func_pointonsurface Create_func_pointonsurface::s_singleton;
+
+Item*
+Create_func_pointonsurface::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_pointonsurface(arg1);
+}
+#endif /*HAVE_SPATIAL*/
 
 
 Create_func_char_length Create_func_char_length::s_singleton;
@@ -3819,6 +3903,15 @@ Create_func_envelope::create_1_arg(THD *thd, Item *arg1)
 {
   return new (thd->mem_root) Item_func_envelope(arg1);
 }
+
+
+Create_func_boundary Create_func_boundary::s_singleton;
+
+Item*
+Create_func_boundary::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_boundary(arg1);
+}
 #endif
 
 
@@ -4329,6 +4422,15 @@ Create_func_interiorringn::create_2_arg(THD *thd, Item *arg1, Item *arg2)
 
 
 #ifdef HAVE_SPATIAL
+Create_func_relate Create_func_relate::s_singleton;
+
+Item*
+Create_func_relate::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *matrix)
+{
+  return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2, matrix);
+}
+
+
 Create_func_mbr_intersects Create_func_mbr_intersects::s_singleton;
 
 Item*
@@ -4429,10 +4531,17 @@ Create_func_isclosed::create_1_arg(THD *thd, Item *arg1)
 {
   return new (thd->mem_root) Item_func_isclosed(arg1);
 }
-#endif
 
 
-#ifdef HAVE_SPATIAL
+Create_func_isring Create_func_isring::s_singleton;
+
+Item*
+Create_func_isring::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_isring(arg1);
+}
+
+
 Create_func_isempty Create_func_isempty::s_singleton;
 
 Item*
@@ -4440,7 +4549,7 @@ Create_func_isempty::create_1_arg(THD *thd, Item *arg1)
 {
   return new (thd->mem_root) Item_func_isempty(arg1);
 }
-#endif
+#endif /*HAVE_SPATIAL*/
 
 
 Create_func_isnull Create_func_isnull::s_singleton;
@@ -5656,6 +5765,7 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)},
   { { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
   { { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
+  { { C_STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
   { { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
   { { C_STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)},
   { { C_STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)},
@@ -5673,6 +5783,7 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)},
   { { C_STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)},
   { { C_STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)},
+  { { C_STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
   { { C_STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)},
   { { C_STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)},
   { { C_STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)},
@@ -5737,6 +5848,7 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
   { { C_STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
   { { C_STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)},
+  { { C_STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)},
   { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
   { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)},
   { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)},
@@ -5803,6 +5915,7 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
   { { C_STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
   { { C_STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)},
+  { { C_STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
   { { C_STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
   { { C_STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
   { { C_STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
@@ -5839,9 +5952,11 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
   { { C_STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
   { { C_STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
+  { { C_STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
   { { C_STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
   { { C_STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
   { { C_STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)},
+  { { C_STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
   { { C_STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
   { { C_STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)},
   { { C_STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
@@ -5870,6 +5985,7 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)},
   { { C_STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
   { { C_STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
+  { { C_STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)},
   { { C_STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
   { { C_STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)},
   { { C_STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
@@ -5883,10 +5999,12 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
   { { C_STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
   { { C_STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)},
+  { { C_STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
   { { C_STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
   { { C_STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
   { { C_STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
   { { C_STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+  { { C_STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)},
   { { C_STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)},
   { { C_STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
   { { C_STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)},
diff --git a/sql/item_func.h b/sql/item_func.h
index 6c9d7e8..bc5f15e 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -61,7 +61,7 @@ class Item_func :public Item_result_field
 		  SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC,
 		  SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
 		  SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
-		  SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
+		  SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, SP_RELATE_FUNC,
                   NOT_FUNC, NOT_ALL_FUNC,
                   NOW_FUNC, TRIG_COND_FUNC,
                   SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index d9200b3..d387297 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -218,6 +218,130 @@ String *Item_func_envelope::val_str(String *str)
 }
 
 
+int Item_func_boundary::Transporter::single_point(double x, double y)
+{
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_line()
+{
+  n_points= 0;
+  current_type= Gcalc_function::shape_line;
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::complete_line()
+{
+  current_type= (Gcalc_function::shape_type) 0;
+  if (n_points > 1)
+    return m_receiver->single_point(last_x, last_y);
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_poly()
+{
+  current_type= Gcalc_function::shape_polygon;
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::complete_poly()
+{
+  current_type= (Gcalc_function::shape_type) 0;
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_ring()
+{
+  n_points= 0;
+  return m_receiver->start_shape(Gcalc_function::shape_line);
+}
+
+
+int Item_func_boundary::Transporter::complete_ring()
+{
+  if (n_points > 1)
+  {
+     m_receiver->add_point(last_x, last_y);
+  }
+  m_receiver->complete_shape();
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::add_point(double x, double y)
+{
+  ++n_points;
+  if (current_type== Gcalc_function::shape_polygon)
+  {
+    /* Polygon's ring case */
+    if (n_points == 1)
+    {
+      last_x= x;
+      last_y= y;
+    }
+    return m_receiver->add_point(x, y);
+  }
+  
+  if (current_type== Gcalc_function::shape_line)
+  {
+    /* Line's case */
+    last_x= x;
+    last_y= y;
+    if (n_points == 1)
+      return m_receiver->single_point(x, y);
+  }
+  return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_collection(int n_objects)
+{
+  return 0;
+}
+
+
+String *Item_func_boundary::val_str(String *str_value)
+{
+  DBUG_ENTER("Item_func_boundary::val_str");
+  DBUG_ASSERT(fixed == 1);
+  String arg_val;
+  String *swkb= args[0]->val_str(&arg_val);
+  Geometry_buffer buffer;
+  Geometry *g;
+  uint32 srid= 0;
+  Transporter trn(&res_receiver);
+  
+  if ((null_value=
+       args[0]->null_value ||
+       !(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+    DBUG_RETURN(0);
+  
+  if (g->store_shapes(&trn))
+    goto mem_error;
+
+  str_value->set_charset(&my_charset_bin);
+  if (str_value->reserve(SRID_SIZE, 512))
+    goto mem_error;
+  str_value->length(0);
+  str_value->q_append(srid);
+
+  if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+    goto mem_error;
+
+  res_receiver.reset();
+  DBUG_RETURN(str_value);
+
+mem_error:
+  null_value= 1;
+  DBUG_RETURN(0);
+}
+
+
 Field::geometry_type Item_func_centroid::get_geometry_type() const
 {
   return Field::GEOM_POINT;
@@ -248,6 +372,289 @@ String *Item_func_centroid::val_str(String *str)
 }
 
 
+int Item_func_convexhull::add_node_to_line(ch_node **p_cur, int dir,
+                                           const Gcalc_heap::Info *pi)
+{
+  ch_node *new_node;
+  ch_node *cur= *p_cur;
+
+  while (cur->prev)
+  {
+    int v_sign= Gcalc_scan_iterator::point::cmp_dx_dy(
+                  cur->prev->pi, cur->pi, cur->pi, pi);
+    if (v_sign*dir <0)
+      break;
+    new_node= cur;
+    cur= cur->prev;
+    res_heap.free_item(new_node);
+  }
+  if (!(new_node= new_ch_node()))
+    return 1;
+  cur->next= new_node;
+  new_node->prev= cur;
+  new_node->pi= pi;
+  *p_cur= new_node;
+  return 0;
+}
+
+
+#ifndef HEAVY_CONVEX_HULL
+String *Item_func_convexhull::val_str(String *str_value)
+{
+  Geometry_buffer buffer;
+  Geometry *geom= NULL;
+  MBR mbr;
+  const char *c_end;
+  Gcalc_operation_transporter trn(&func, &collector);
+  uint32 srid= 0;
+  ch_node *left_first, *left_cur, *right_first, *right_cur;
+  Gcalc_heap::Info *cur_pi;
+  
+  DBUG_ENTER("Item_func_convexhull::val_str");
+  DBUG_ASSERT(fixed == 1);
+  String *swkb= args[0]->val_str(&tmp_value);
+
+  if ((null_value=
+       args[0]->null_value ||
+       !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+    DBUG_RETURN(0);
+  
+  geom->get_mbr(&mbr, &c_end);
+  collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+  if ((null_value= geom->store_shapes(&trn)))
+  {
+    str_value= 0;
+    goto mem_error;
+  }
+
+  collector.prepare_operation();
+  if (!(cur_pi= collector.get_first()))
+    goto build_result; /* An EMPTY GEOMETRY */
+
+  if (!cur_pi->get_next())
+  {
+    /* Single point. */
+    if (res_receiver.single_point(cur_pi->x, cur_pi->y))
+      goto mem_error;
+    goto build_result;
+  }
+
+  left_cur= left_first= new_ch_node();
+  right_cur= right_first= new_ch_node();
+  right_first->prev= left_first->prev= 0;
+  right_first->pi= left_first->pi= cur_pi;
+
+  while ((cur_pi= cur_pi->get_next()))
+  {
+    /* Handle left part of the hull, then the right part. */
+    if (add_node_to_line(&left_cur, 1, cur_pi))
+      goto mem_error;
+    if (add_node_to_line(&right_cur, -1, cur_pi))
+      goto mem_error;
+  }
+
+  left_cur->next= 0;
+  if (left_first->get_next()->get_next() == NULL &&
+      right_cur->prev->prev == NULL)
+  {
+    /* We only have 2 nodes in the result, so we create a polyline. */
+    if (res_receiver.start_shape(Gcalc_function::shape_line) ||
+        res_receiver.add_point(left_first->pi->x, left_first->pi->y) ||
+        res_receiver.add_point(left_cur->pi->x, left_cur->pi->y) ||
+        res_receiver.complete_shape())
+
+      goto mem_error;
+
+    goto build_result;
+  }
+
+  if (res_receiver.start_shape(Gcalc_function::shape_polygon))
+    goto mem_error;
+
+  while (left_first)
+  {
+    if (res_receiver.add_point(left_first->pi->x, left_first->pi->y))
+      goto mem_error;
+    left_first= left_first->get_next();
+  }
+
+  /* Skip last point in the right part as it coincides */
+  /* with the last one in the left.                    */
+  right_cur= right_cur->prev;
+  while (right_cur->prev)
+  {
+    if (res_receiver.add_point(right_cur->pi->x, right_cur->pi->y))
+      goto mem_error;
+    right_cur= right_cur->prev;
+  }
+  res_receiver.complete_shape();
+
+build_result:
+  str_value->set_charset(&my_charset_bin);
+  if (str_value->reserve(SRID_SIZE, 512))
+    goto mem_error;
+  str_value->length(0);
+  str_value->q_append(srid);
+
+  if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+    goto mem_error;
+
+mem_error:
+  collector.reset();
+  func.reset();
+  res_receiver.reset();
+  res_heap.reset();
+  DBUG_RETURN(str_value);
+}
+
+#else /*HEAVY_CONVEX_HULL*/
+String *Item_func_convexhull::val_str(String *str_value)
+{
+  Geometry_buffer buffer;
+  Geometry *geom= NULL;
+  MBR mbr;
+  const char *c_end;
+  Gcalc_operation_transporter trn(&func, &collector);
+  const Gcalc_scan_iterator::event_point *ev;
+  uint32 srid= 0;
+  ch_node *left_first, *left_cur, *right_first, *right_cur;
+  
+  DBUG_ENTER("Item_func_convexhull::val_str");
+  DBUG_ASSERT(fixed == 1);
+  String *swkb= args[0]->val_str(&tmp_value);
+
+  if ((null_value=
+       args[0]->null_value ||
+       !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+    DBUG_RETURN(0);
+  
+  geom->get_mbr(&mbr, &c_end);
+  collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+  if ((null_value= geom->store_shapes(&trn)))
+  {
+    str_value= 0;
+    goto mem_error;
+  }
+
+  collector.prepare_operation();
+  scan_it.init(&collector);
+  scan_it.killed= (int *) &(current_thd->killed);
+
+  if (!scan_it.more_points())
+    goto build_result; /* An EMPTY GEOMETRY */
+
+  if (scan_it.step())
+    goto mem_error;
+
+  if (!scan_it.more_points())
+  {
+    /* Single point. */
+    if (res_receiver.single_point(scan_it.get_events()->pi->x,
+                                  scan_it.get_events()->pi->y))
+      goto mem_error;
+    goto build_result;
+  }
+
+  left_cur= left_first= new_ch_node();
+  right_cur= right_first= new_ch_node();
+  right_first->prev= left_first->prev= 0;
+  right_first->pi= left_first->pi= scan_it.get_events()->pi;
+
+  while (scan_it.more_points())
+  {
+    if (scan_it.step())
+      goto mem_error;
+    ev= scan_it.get_events();
+    
+    /* Skip the intersections-only events. */
+    while (ev->event == scev_intersection)
+    {
+      ev= ev->get_next();
+      if (!ev)
+        goto skip_point;
+    }
+
+    {
+      Gcalc_point_iterator pit(&scan_it);
+      if (!pit.point() || scan_it.get_event_position() == pit.point())
+      {
+        /* Handle left part of the hull. */
+        if (add_node_to_line(&left_cur, 1, ev->pi))
+          goto mem_error;
+      }
+      if (pit.point())
+      {
+        /* Check the rightmost point */
+        for(; pit.point()->c_get_next(); ++pit)
+          ;
+      }
+      if (!pit.point() || pit.point()->event ||
+          scan_it.get_event_position() == pit.point()->c_get_next())
+      {
+        /* Handle right part of the hull. */
+        if (add_node_to_line(&right_cur, -1, ev->pi))
+          goto mem_error;
+      }
+    }
+skip_point:;
+  }
+
+  left_cur->next= 0;
+  if (left_first->get_next()->get_next() == NULL &&
+      right_cur->prev->prev == NULL)
+  {
+    /* We only have 2 nodes in the result, so we create a polyline. */
+    if (res_receiver.start_shape(Gcalc_function::shape_line) ||
+        res_receiver.add_point(left_first->pi->x, left_first->pi->y) ||
+        res_receiver.add_point(left_cur->pi->x, left_cur->pi->y) ||
+        res_receiver.complete_shape())
+
+      goto mem_error;
+
+    goto build_result;
+  }
+
+  if (res_receiver.start_shape(Gcalc_function::shape_polygon))
+    goto mem_error;
+
+  while (left_first)
+  {
+    if (res_receiver.add_point(left_first->pi->x, left_first->pi->y))
+      goto mem_error;
+    left_first= left_first->get_next();
+  }
+
+  /* Skip last point in the right part as it coincides */
+  /* with the last one in the left.                    */
+  right_cur= right_cur->prev;
+  while (right_cur->prev)
+  {
+    if (res_receiver.add_point(right_cur->pi->x, right_cur->pi->y))
+      goto mem_error;
+    right_cur= right_cur->prev;
+  }
+  res_receiver.complete_shape();
+
+build_result:
+  str_value->set_charset(&my_charset_bin);
+  if (str_value->reserve(SRID_SIZE, 512))
+    goto mem_error;
+  str_value->length(0);
+  str_value->q_append(srid);
+
+  if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+    goto mem_error;
+
+mem_error:
+  collector.reset();
+  func.reset();
+  res_receiver.reset();
+  res_heap.reset();
+  DBUG_RETURN(str_value);
+}
+#endif /*HEAVY_CONVEX_HULL*/
+
+
 /*
   Spatial decomposition functions
 */
@@ -593,12 +1000,17 @@ longlong Item_func_spatial_mbr_rel::val_int()
 
 Item_func_spatial_rel::Item_func_spatial_rel(Item *a,Item *b,
                                              enum Functype sp_rel) :
-    Item_bool_func2(a,b), collector()
+    Item_int_func(a,b), collector()
 {
   spatial_rel = sp_rel;
 }
 
 
+Item_func_spatial_rel::Item_func_spatial_rel(Item *a,Item *b, Item *mask) :
+    Item_int_func(a,b,mask), spatial_rel(SP_RELATE_FUNC)
+{}
+
+
 Item_func_spatial_rel::~Item_func_spatial_rel()
 {
 }
@@ -623,6 +1035,8 @@ const char *Item_func_spatial_rel::func_name() const
       return "st_crosses";
     case SP_OVERLAPS_FUNC:
       return "st_overlaps";
+    case SP_RELATE_FUNC:
+      return "st_relate";
     default:
       DBUG_ASSERT(0);  // Should never happened
       return "sp_unknown"; 
@@ -661,6 +1075,84 @@ static double distance_points(const Gcalc_heap::Info *a,
 }
 
 
+static Gcalc_function::op_type op_matrix(int n)
+{
+  switch (n)
+  {
+    case 0:
+      return Gcalc_function::op_border;
+    case 1:
+      return Gcalc_function::op_internals;
+    case 2:
+      return (Gcalc_function::op_type)
+        ((int) Gcalc_function::op_not | (int) Gcalc_function::op_union);
+  };
+  GCALC_DBUG_ASSERT(FALSE);
+  return Gcalc_function::op_any;
+}
+
+
+static int setup_relate_func(Geometry *g1, Geometry *g2,
+    Gcalc_operation_transporter *trn, Gcalc_function *func,
+    const char *mask)
+{
+  int do_store_shapes=1;
+  uint shape_a, shape_b;
+  uint n_operands= 0;
+  int last_shape_pos;
+
+  last_shape_pos= func->get_next_expression_pos();
+  func->add_operation(Gcalc_function::op_intersection, 0);
+  for (int nc=0; nc<9; nc++)
+  {
+    uint cur_op;
+
+    cur_op= Gcalc_function::op_intersection;
+    switch (mask[nc])
+    {
+      case '*':
+        continue;
+      case 'T':
+      case '0':
+      case '1':
+      case '2':
+        cur_op|= Gcalc_function::v_find_t;
+        break;
+      case 'F':
+        cur_op|= Gcalc_function::v_find_f;
+        break;
+    };
+    ++n_operands;
+    if (func->reserve_op_buffer(1))
+      return 1;
+    func->add_operation(cur_op, 2);
+
+    func->add_operation(op_matrix(nc/3), 1);
+    if (do_store_shapes)
+    {
+      shape_a= func->get_next_expression_pos();
+      if (g1->store_shapes(trn))
+        return 1;
+    }
+    else
+      func->repeat_expression(shape_a);
+    func->add_operation(op_matrix(nc%3), 1);
+    if (do_store_shapes)
+    {
+      shape_b= func->get_next_expression_pos();
+      if (g2->store_shapes(trn))
+        return 1;
+      do_store_shapes= 0;
+    }
+    else
+      func->repeat_expression(shape_b);
+  }
+  
+  func->add_operands_to_op(last_shape_pos, n_operands);
+  return 0;
+}
+
+
 #define GIS_ZERO 0.00000000001
 
 longlong Item_func_spatial_rel::val_int()
@@ -669,6 +1161,7 @@ longlong Item_func_spatial_rel::val_int()
   DBUG_ASSERT(fixed == 1);
   String *res1;
   String *res2;
+  String *res3;
   Geometry_buffer buffer1, buffer2;
   Geometry *g1, *g2;
   int result= 0;
@@ -736,6 +1229,8 @@ longlong Item_func_spatial_rel::val_int()
     case SP_OVERLAPS_FUNC:
     case SP_CROSSES_FUNC:
       func.add_operation(Gcalc_function::op_intersection, 2);
+      if (func.reserve_op_buffer(1))
+        break;
       func.add_operation(Gcalc_function::v_find_t |
                          Gcalc_function::op_intersection, 2);
       shape_a= func.get_next_expression_pos();
@@ -744,6 +1239,8 @@ longlong Item_func_spatial_rel::val_int()
       shape_b= func.get_next_expression_pos();
       if ((null_value= g2->store_shapes(&trn)))
         break;
+      if (func.reserve_op_buffer(7))
+        break;
       func.add_operation(Gcalc_function::v_find_t |
                          Gcalc_function::op_intersection, 2);
       func.add_operation(Gcalc_function::v_find_t |
@@ -756,6 +1253,8 @@ longlong Item_func_spatial_rel::val_int()
       func.repeat_expression(shape_a);
       break;
     case SP_TOUCHES_FUNC:
+      if (func.reserve_op_buffer(2))
+        break;
       func.add_operation(Gcalc_function::op_intersection, 2);
       func.add_operation(Gcalc_function::v_find_f |
                          Gcalc_function::op_not |
@@ -775,6 +1274,13 @@ longlong Item_func_spatial_rel::val_int()
       func.add_operation(Gcalc_function::op_border, 1);
       func.repeat_expression(shape_b);
       break;
+    case SP_RELATE_FUNC:
+      res3= args[2]->val_str(&tmp_matrix);
+      if ((null_value= args[2]->null_value))
+        break;
+      null_value= (res3->length() != 9) ||
+        setup_relate_func(g1, g2, &trn, &func, res3->ptr());
+      break;
     default:
       DBUG_ASSERT(FALSE);
       break;
@@ -859,7 +1365,7 @@ String *Item_func_spatial_operation::val_str(String *str_value)
   str_value->length(0);
   str_value->q_append(srid);
 
-  if (Geometry::create_from_opresult(&buffer1, str_value, res_receiver))
+  if (!Geometry::create_from_opresult(&buffer1, str_value, res_receiver))
     goto exit;
 
 exit:
@@ -1319,7 +1825,7 @@ String *Item_func_buffer::val_str(String *str_value)
   str_value->length(0);
   str_value->q_append(srid);
 
-  if (Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+  if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
     goto mem_error;
 
   null_value= 0;
@@ -1423,6 +1929,30 @@ longlong Item_func_isclosed::val_int()
   return (longlong) isclosed;
 }
 
+
+longlong Item_func_isring::val_int()
+{
+  /* It's actually a combination of two functions - IsClosed and IsSimple */
+  DBUG_ASSERT(fixed == 1);
+  String tmp;
+  String *swkb= args[0]->val_str(&tmp);
+  Geometry_buffer buffer;
+  Geometry *geom;
+  int isclosed= 0;				// In case of error
+
+  null_value= (!swkb || 
+	       args[0]->null_value ||
+	       !(geom=
+		 Geometry::construct(&buffer, swkb->ptr(), swkb->length())) ||
+	       geom->is_closed(&isclosed));
+
+  if (!isclosed)
+    return 0;
+
+  return Item_func_issimple::val_int();
+}
+
+
 /*
   Numerical functions
 */
@@ -1739,6 +2269,121 @@ double Item_func_distance::val_real()
 }
 
 
+String *Item_func_pointonsurface::val_str(String *str)
+{
+  Gcalc_operation_transporter trn(&func, &collector);
+
+  DBUG_ENTER("Item_func_pointonsurface::val_real");
+  DBUG_ASSERT(fixed == 1);
+  String *res= args[0]->val_str(&tmp_value);
+  Geometry_buffer buffer;
+  Geometry *g;
+  MBR mbr;
+  const char *c_end;
+  double px, py, x0, y0;
+  String *result= 0;
+  const Gcalc_scan_iterator::point *pprev= NULL;
+  uint32 srid;
+
+
+  null_value= 1;
+  if ((args[0]->null_value ||
+       !(g= Geometry::construct(&buffer, res->ptr(), res->length())) ||
+       g->get_mbr(&mbr, &c_end)))
+    goto mem_error;
+
+  collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+
+  if (g->store_shapes(&trn))
+    goto mem_error;
+
+  collector.prepare_operation();
+  scan_it.init(&collector);
+
+  while (scan_it.more_points())
+  {
+    if (scan_it.step())
+      goto mem_error;
+
+    if (scan_it.get_h() > GIS_ZERO)
+    {
+      y0= scan_it.get_y();
+      break;
+    }
+  }
+
+  if (!scan_it.more_points())
+  {
+    goto exit;
+  }
+
+  if (scan_it.step())
+    goto mem_error;
+
+  for (Gcalc_point_iterator pit(&scan_it); pit.point(); ++pit)
+  {
+    if (pprev == NULL)
+    {
+      pprev= pit.point();
+      continue;
+    }
+    x0= scan_it.get_sp_x(pprev);
+    px= scan_it.get_sp_x(pit.point());
+    if (px - x0 > GIS_ZERO)
+    {
+      if (scan_it.get_h() > GIS_ZERO)
+      {
+        px= (px + x0) / 2.0;
+        py= scan_it.get_y();
+      }
+      else
+      {
+        px= (px + x0) / 2.0;
+        py= (y0 + scan_it.get_y()) / 2.0;
+      }
+      null_value= 0;
+      break;
+    }
+    pprev= NULL;
+  }
+
+  if (null_value)
+    goto exit;
+
+  str->set_charset(&my_charset_bin);
+  if (str->reserve(SRID_SIZE, 512))
+    goto mem_error;
+
+  str->length(0);
+  srid= uint4korr(res->ptr());
+  str->q_append(srid);
+
+  if (Geometry::create_point(str, px, py))
+    goto mem_error;
+
+  result= str;
+
+exit:
+  collector.reset();
+  func.reset();
+  scan_it.reset();
+  DBUG_RETURN(result);
+
+mem_error:
+  collector.reset();
+  func.reset();
+  scan_it.reset();
+  null_value= 1;
+  DBUG_RETURN(0);
+}
+
+
+Field::geometry_type Item_func_pointonsurface::get_geometry_type() const
+{
+  return Field::GEOM_POINT;
+}
+
+
 #ifndef DBUG_OFF
 longlong Item_func_gis_debug::val_int()
 {
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 6d52661..64863ed 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -93,6 +93,39 @@ class Item_func_geometry_type: public Item_str_ascii_func
   };
 };
 
+
+// #define HEAVY_CONVEX_HULL
+class Item_func_convexhull: public Item_geometry_func
+{
+  class ch_node: public Gcalc_dyn_list::Item
+  {
+  public:
+    const Gcalc_heap::Info *pi;
+    ch_node *prev;
+    Gcalc_dyn_list::Item *next;
+    ch_node *get_next() { return (ch_node *) next; }
+  };
+
+  Gcalc_heap collector;
+  Gcalc_function func;
+  Gcalc_dyn_list res_heap;
+
+  Gcalc_result_receiver res_receiver;
+  String tmp_value;
+#ifdef HEAVY_CONVEX_HULL
+  Gcalc_scan_iterator scan_it;
+#endif /*HEAVY_CONVEX_HULL*/
+  ch_node *new_ch_node() { return (ch_node *) res_heap.new_item(); }
+  int add_node_to_line(ch_node **p_cur, int dir, const Gcalc_heap::Info *pi);
+public:
+  Item_func_convexhull(Item *a): Item_geometry_func(a),
+    res_heap(8192, sizeof(ch_node))
+    {}
+  const char *func_name() const { return "st_convexhull"; }
+  String *val_str(String *);
+};
+
+
 class Item_func_centroid: public Item_geometry_func
 {
 public:
@@ -111,6 +144,38 @@ class Item_func_envelope: public Item_geometry_func
   Field::geometry_type get_geometry_type() const;
 };
 
+
+class Item_func_boundary: public Item_geometry_func
+{
+  class Transporter : public Gcalc_shape_transporter
+  {
+    Gcalc_result_receiver *m_receiver;
+    uint n_points;
+    Gcalc_function::shape_type current_type;
+    double last_x, last_y;
+  public:
+    Transporter(Gcalc_result_receiver *receiver) :
+      Gcalc_shape_transporter(NULL), m_receiver(receiver)
+    {}
+    int single_point(double x, double y);
+    int start_line();
+    int complete_line();
+    int start_poly();
+    int complete_poly();
+    int start_ring();
+    int complete_ring();
+    int add_point(double x, double y);
+
+    int start_collection(int n_objects);
+  };
+  Gcalc_result_receiver res_receiver;
+public:
+  Item_func_boundary(Item *a): Item_geometry_func(a) {}
+  const char *func_name() const { return "st_boundary"; }
+  String *val_str(String *);
+};
+
+
 class Item_func_point: public Item_geometry_func
 {
 public:
@@ -229,15 +294,16 @@ class Item_func_spatial_mbr_rel: public Item_bool_func2
 };
 
 
-class Item_func_spatial_rel: public Item_bool_func2
+class Item_func_spatial_rel: public Item_int_func
 {
   enum Functype spatial_rel;
   Gcalc_heap collector;
   Gcalc_scan_iterator scan_it;
   Gcalc_function func;
-  String tmp_value1,tmp_value2;
+  String tmp_value1,tmp_value2, tmp_matrix;
 public:
   Item_func_spatial_rel(Item *a,Item *b, enum Functype sp_rel);
+  Item_func_spatial_rel(Item *a, Item *b, Item *matrix);
   virtual ~Item_func_spatial_rel();
   longlong val_int();
   enum Functype functype() const 
@@ -253,6 +319,9 @@ class Item_func_spatial_rel: public Item_bool_func2
 
   void fix_length_and_dec() { maybe_null= 1; }
   bool is_null() { (void) val_int(); return null_value; }
+  bool is_bool_func() { return 1; }
+  uint decimal_precision() const { return 1; }
+  optimize_type select_optimize() const { return OPTIMIZE_OP; }
 };
 
 
@@ -369,6 +438,14 @@ class Item_func_isclosed: public Item_bool_func
   void fix_length_and_dec() { maybe_null= 1; }
 };
 
+class Item_func_isring: public Item_func_issimple
+{
+public:
+  Item_func_isring(Item *a): Item_func_issimple(a) {}
+  longlong val_int();
+  const char *func_name() const { return "st_isring"; }
+};
+
 class Item_func_dimension: public Item_int_func
 {
   String value;
@@ -497,6 +574,20 @@ class Item_func_distance: public Item_real_func
 };
 
 
+class Item_func_pointonsurface: public Item_geometry_func
+{
+  String tmp_value;
+  Gcalc_heap collector;
+  Gcalc_function func;
+  Gcalc_scan_iterator scan_it;
+public:
+  Item_func_pointonsurface(Item *a): Item_geometry_func(a) {}
+  const char *func_name() const { return "st_pointonsurface"; }
+  String *val_str(String *);
+  Field::geometry_type get_geometry_type() const;
+};
+
+
 #ifndef DBUG_OFF
 class Item_func_gis_debug: public Item_int_func
 {
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 34d2417..9f67280 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -291,19 +291,18 @@ Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
 }
 
 
-int Geometry::create_from_opresult(Geometry_buffer *g_buf,
+Geometry *Geometry::create_from_opresult(Geometry_buffer *g_buf,
                                    String *res, Gcalc_result_receiver &rr)
 {
   uint32 geom_type= rr.get_result_typeid();
   Geometry *obj= create_by_typeid(g_buf, geom_type);
 
   if (!obj || res->reserve(WKB_HEADER_SIZE, 512))
-    return 1;
+    return NULL;
 
   res->q_append((char) wkb_ndr);
   res->q_append(geom_type);
-  return obj->init_from_opresult(res, rr.result(), rr.length()) == 0 &&
-         rr.length();
+  return obj->init_from_opresult(res, rr.result(), rr.length()) ? obj : NULL;
 }
 
 
@@ -386,7 +385,7 @@ bool Geometry::create_point(String *result, const char *data) const
     1	Can't reallocate 'result'
 */
 
-bool Geometry::create_point(String *result, double x, double y) const
+bool Geometry::create_point(String *result, double x, double y)
 {
   if (result->reserve(1 + 4 + POINT_DATA_SIZE))
     return 1;
@@ -2221,6 +2220,13 @@ uint Gis_geometry_collection::init_from_opresult(String *bin,
     return 0;
   bin->q_append(n_objects);
   
+  if (res_len == 0)
+  {
+    /* Special case of GEOMETRYCOLLECTION EMPTY. */
+    opres+= 1;
+    goto empty_geom;
+  }
+  
   while (res_len)
   {
     switch ((Gcalc_function::shape_type) uint4korr(opres))
@@ -2244,6 +2250,7 @@ uint Gis_geometry_collection::init_from_opresult(String *bin,
     res_len-= g_len;
     n_objects++;
   }
+empty_geom:
   bin->write_at_position(no_pos, n_objects);
   return (uint) (opres - opres_orig);
 }
diff --git a/sql/spatial.h b/sql/spatial.h
index 3a6055a..b850d40 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -298,7 +298,7 @@ class Geometry
 				   bool init_stream=1);
   static Geometry *create_from_wkb(Geometry_buffer *buffer,
                                    const char *wkb, uint32 len, String *res);
-  static int create_from_opresult(Geometry_buffer *g_buf,
+  static Geometry *create_from_opresult(Geometry_buffer *g_buf,
                                   String *res, Gcalc_result_receiver &rr);
   int as_wkt(String *wkt, const char **end);
 
@@ -316,6 +316,7 @@ class Geometry
   bool envelope(String *result) const;
   static Class_info *ci_collection[wkb_last+1];
 
+  static bool create_point(String *result, double x, double y);
 protected:
   static Class_info *find_class(int type_id)
   {
@@ -326,7 +327,6 @@ class Geometry
   const char *append_points(String *txt, uint32 n_points,
 			    const char *data, uint32 offset) const;
   bool create_point(String *result, const char *data) const;
-  bool create_point(String *result, double x, double y) const;
   const char *get_mbr_for_points(MBR *mbr, const char *data, uint offset)
     const;
 


More information about the commits mailing list