From f710d6ca3f8d1de34ba8c6140dd773735cc80d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=AA=E4=BC=9A=E6=89=93log?= <83819142+hanwen-sun@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:14:01 +0800 Subject: [PATCH] support spatial function (#378) * fix datetime&audit_log bug * start spatial * test for data_type done * add comment for spatial * update ci * support schema for spatial * test alter field done * test field_extractor done * test field_data_helper * fix ci images bug * support spatial data convert * begin spatial support * fix cpplint * fix cppplugin * add test for spatial * fix cmakelists * improve spatial comments * support big endian and comments * fix big endian bug for spatial data * support spatial data for protobuf * add function for spatial type * spatial bug * add test for spatial * fix error for spatial test * fix loadfromzip cppplugin * modify for merge * reset files * add pytest for spatial data * modify pyunit_test * fix spatial test * enhance spatial data tests * add more test for spatial data * enhance embedded_api_unittest * support spatial data creation * support built-in function & procedure for spatial data * support spatial function * modify head file * reslove confilts between boost and cpprest * move boost/geometry.hpp from header file to cpp file * fix it test * restore sortstr.zip * add comments * fix cpplint * Update cypher_types.h * modify code style * fix python intergation bug * fix intergation * fix ci images * begin docs of spatial data * fix throw bug * fix cpplint * fix procedure --------- Co-authored-by: Shipeng Qi Co-authored-by: Shipeng Qi Co-authored-by: lipanpan03 <41904587+lipanpan03@users.noreply.github.com> --- docs/en-US/source/2.introduction/4.schema.md | 3 + docs/zh-CN/source/2.introduction/4.schema.md | 3 + include/lgraph/lgraph_spatial.h | 100 ++++++- include/lgraph/lgraph_types.h | 7 +- src/core/lgraph_spatial.cpp | 133 ++++++++- src/cypher/arithmetic/agg_funcs.h | 40 +++ .../arithmetic/arithmetic_expression.cpp | 271 ++++++++++++++++++ src/cypher/arithmetic/arithmetic_expression.h | 59 ++++ src/cypher/cypher_types.h | 11 + src/cypher/filter/filter.h | 40 +++ src/cypher/parser/cypher_base_visitor.h | 1 + src/cypher/procedure/procedure.cpp | 52 ++++ src/cypher/procedure/procedure.h | 12 + src/cypher/resultset/record.h | 8 + test/integration/test_ha_procedure.py | 2 +- test/integration/test_procedure.py | 2 +- test/test_cypher.cpp | 86 +++++- 17 files changed, 802 insertions(+), 28 deletions(-) diff --git a/docs/en-US/source/2.introduction/4.schema.md b/docs/en-US/source/2.introduction/4.schema.md index 422d96b67..b25e0bf1c 100644 --- a/docs/en-US/source/2.introduction/4.schema.md +++ b/docs/en-US/source/2.introduction/4.schema.md @@ -37,6 +37,9 @@ TuGraph Supports a variety of data types that can be used as attributes, the spe | DOUBLE | | | 64-bit float | | STRING | | | A string of variable length | | BLOB | | | Binary data | +| POINT | | | EWKB format data of point | +| LINESTRING | | | EWKB format data of linestring | +| POLYGON | | | EWKB format data of polygon | _BLOB data is BASE64 encoded in input and output_ diff --git a/docs/zh-CN/source/2.introduction/4.schema.md b/docs/zh-CN/source/2.introduction/4.schema.md index dd63e22d4..f0f61b370 100644 --- a/docs/zh-CN/source/2.introduction/4.schema.md +++ b/docs/zh-CN/source/2.introduction/4.schema.md @@ -36,6 +36,9 @@ TuGraph支持多种可用于属性的数据类型。具体支持的数据类型 | DOUBLE | | | 64位浮点数 | | STRING | | | 不定长度的字符串 | | BLOB | | | 二进制数据(在输入输出时使用Base64编码) | +| POINT | | | EWKB格式数据,表示点 | +| LINESTRING | | | EWKB格式数据,表示线 | +| POLYGON | | | EWKB格式数据,表示面(多边形) | ### 1.3. 索引 diff --git a/include/lgraph/lgraph_spatial.h b/include/lgraph/lgraph_spatial.h index a24ea0aef..1c7703279 100644 --- a/include/lgraph/lgraph_spatial.h +++ b/include/lgraph/lgraph_spatial.h @@ -8,7 +8,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /** - * @file lgraph_spatial.h + * @file lgraph_spatial.h * @brief Implemnets the Spatial, SpatialBase and SpatialDerive classes. * * TODO(shw): @@ -30,7 +30,6 @@ #include #include #include - #include #include @@ -328,9 +327,14 @@ class Spatial { return type_; } - bool operator==(const Spatial& other); + /** + * @brief return the distance between two spatial data; + * + * @param other the other data of spatial type; + */ + double Distance(Spatial& other); - // double Distance(geography& other); + bool operator==(const Spatial& other); }; /** @@ -347,14 +351,10 @@ class SpatialBase { */ SpatialBase(SRID srid, SpatialType type) : srid_(srid), type_(type) {} - // virtual ~SpatialBase(); - virtual std::string AsEWKB() const = 0; virtual std::string AsEWKT() const = 0; - // virtual std::string ToString() const = 0; - SpatialType GetType() const { return type_; } @@ -365,7 +365,7 @@ class SpatialBase { }; /** - * @brief implements a Point spatial class which spatial data is Point; + * @brief implements a Point spatial class which spatial type is Point; */ template class Point : public SpatialBase { @@ -399,6 +399,17 @@ class Point : public SpatialBase { */ explicit Point(const std::string& ewkb); + /** + * @brief construct Point from Coordinate pairs and srid; + * + * @param arg1 the first coordinate datum; + * + * @param arg2 the second coordinate datum; + * + * @param srid the srid of the Point; + */ + Point(double arg1, double arg2, SRID& srid); + std::string AsEWKB() const override { return ewkb_; } @@ -416,6 +427,27 @@ class Point : public SpatialBase { return point_; } + /** + * @brief caculate the distance between data of point type; + * + * @param other the other data of point type; + */ + double Distance(Point& other); + + /** + * @brief caculate the distance between point and linestring; + * + * @param other the other data of linestring type; + */ + double Distance(LineString& other); + + /** + * @brief caculate the distance between point and polygon; + * + * @param other the other data of polygon type; + */ + double Distance(Polygon& other); + bool operator==(const Point& other); }; @@ -425,8 +457,8 @@ class Point : public SpatialBase { template class LineString : public SpatialBase { std::string ewkb_; - typedef bg::model::point Point; - bg::model::linestring line_; + typedef bg::model::point Point_; + bg::model::linestring line_; public: LineString(SRID srid, SpatialType type, int construct_type, std::string& content); @@ -448,6 +480,27 @@ class LineString : public SpatialBase { return line_; } + /** + * @brief caculate the distance between linestring and point; + * + * @param other the other data of distance type; + */ + double Distance(Point& other); + + /** + * @brief caculate the distance between linestring and linestring; + * + * @param other the other data of linestring type; + */ + double Distance(LineString& other); + + /** + * @brief caculate the distance between linestring and polygon; + * + * @param other the other data of polygon type; + */ + double Distance(Polygon& other); + bool operator==(const LineString& other); }; @@ -457,8 +510,8 @@ class LineString : public SpatialBase { template class Polygon : public SpatialBase { std::string ewkb_; - typedef bg::model::point Point; - bg::model::polygon polygon_; + typedef bg::model::point Point_; + bg::model::polygon polygon_; public: Polygon(SRID srid, SpatialType type, int construct_type, std::string& content); @@ -480,6 +533,27 @@ class Polygon : public SpatialBase { return polygon_; } + /** + * @brief caculate the distance between linestring and point; + * + * @param other the other data of point type; + */ + double Distance(Point& other); + + /** + * @brief caculate the distance between linestring and linestring; + * + * @param other the other data of linestring type; + */ + double Distance(LineString& other); + + /** + * @brief caculate the distance between linestring and polygon; + * + * @param other the other data of polygon type; + */ + double Distance(Polygon& other); + bool operator==(const Polygon& other); }; } // namespace lgraph_api diff --git a/include/lgraph/lgraph_types.h b/include/lgraph/lgraph_types.h index 188524809..9a61fa91b 100644 --- a/include/lgraph/lgraph_types.h +++ b/include/lgraph/lgraph_types.h @@ -852,14 +852,14 @@ struct FieldData { inline ::lgraph_api::Spatial<::lgraph_api::Wgs84> AsWgsSpatial() const { - if (type == FieldType::SPATIAL) return ::lgraph_api::Spatial + if (IsSpatial()) return ::lgraph_api::Spatial <::lgraph_api::Wgs84>(*data.buf); throw std::bad_cast(); } inline ::lgraph_api::Spatial<::lgraph_api::Cartesian> AsCartesianSpatial() const { - if (type == FieldType::SPATIAL) return ::lgraph_api::Spatial + if (IsSpatial()) return ::lgraph_api::Spatial <::lgraph_api::Cartesian>(*data.buf); throw std::bad_cast(); } @@ -1140,7 +1140,8 @@ struct FieldData { bool IsPolygon() const { return type == FieldType::POLYGON; } /** @brief Query if this object is spatial*/ - bool IsSpatial() const { return type == FieldType::SPATIAL; } + bool IsSpatial() const { return type == FieldType::SPATIAL || IsPoint() || IsLineString() + || IsPolygon(); } private: /** @brief Query if 't' is BLOB or STRING */ diff --git a/src/core/lgraph_spatial.cpp b/src/core/lgraph_spatial.cpp index 3e7588fcb..4fa804e29 100644 --- a/src/core/lgraph_spatial.cpp +++ b/src/core/lgraph_spatial.cpp @@ -14,6 +14,7 @@ #include "lgraph/lgraph_spatial.h" #include "lgraph/lgraph_exceptions.h" +#include namespace lgraph_api { @@ -203,9 +204,6 @@ SRID ExtractSRID(const std::string& ewkb) { } SpatialType ExtractType(const std::string& ewkb) { - // if (EWKB.size() < 50) - // throw InputError("wrong EWKB type"); - std::string type = ewkb.substr(2, 8); // if the input format is EWKB type, we only need the first 2 bytes; if (type[4] != '0' || type[6] != '0') @@ -380,6 +378,56 @@ std::string Spatial::ToString() const { return AsEWKB(); } +template +double Spatial::Distance(Spatial& other) { + switch (type_) { + case SpatialType::POINT: + { + switch (other.GetType()) { + case SpatialType::POINT: + return point_->Distance(*other.GetPoint().get()); + case SpatialType::LINESTRING: + return point_->Distance(*other.GetLine().get()); + case SpatialType::POLYGON: + return point_->Distance(*other.GetPolygon().get()); + default: + throw std::runtime_error("unsupported spatial type!"); + } + } + + case SpatialType::LINESTRING: + { + switch (other.GetType()) { + case SpatialType::POINT: + return line_->Distance(*other.GetPoint().get()); + case SpatialType::LINESTRING: + return line_->Distance(*other.GetLine().get()); + case SpatialType::POLYGON: + return line_->Distance(*other.GetPolygon().get()); + default: + throw std::runtime_error("unsupported spatial type!"); + } + } + + case SpatialType::POLYGON: + { + switch (other.GetType()) { + case SpatialType::POINT: + return polygon_->Distance(*other.GetPoint().get()); + case SpatialType::LINESTRING: + return polygon_->Distance(*other.GetLine().get()); + case SpatialType::POLYGON: + return polygon_->Distance(*other.GetPolygon().get()); + default: + throw std::runtime_error("unsupported spatial type!"); + } + } + + default: + THROW_CODE(InputError, "unsupported spatial type!"); + } +} + template bool Spatial::operator==(const Spatial &other) { return AsEWKB() == other.AsEWKB(); @@ -453,6 +501,22 @@ Point::Point(const std::string& ewkb) transform(ewkb_.begin(), ewkb_.end(), ewkb_.begin(), ::toupper); } +template +Point::Point(double arg1, double arg2, SRID& srid) +: SpatialBase(srid, SpatialType::POINT) { + bg::set<0>(point_, arg1); + bg::set<1>(point_, arg2); + // write wkb; + std::string wkb_out; + bg::write_wkb(point_, std::back_inserter(wkb_out)); + std::string hex_out; + if (!bg::wkb2hex(wkb_out.begin(), wkb_out.end(), hex_out)) + THROW_CODE(InputError, "wrong point data!"); + // set extension + ewkb_ = SetExtension(hex_out, srid); + transform(ewkb_.begin(), ewkb_.end(), ewkb_.begin(), ::toupper); +} + template std::string Point::AsEWKT() const { std::string ewkt; @@ -474,6 +538,27 @@ std::string Point::ToString() const { return AsEWKB(); } +template +double Point::Distance(Point& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(point_, other.GetSpatialData()); +} + +template +double Point::Distance(LineString& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(point_, other.GetSpatialData()); +} + +template +double Point::Distance(Polygon& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(point_, other.GetSpatialData()); +} + template bool Point::operator==(const Point &other) { return AsEWKB() == other.AsEWKB(); @@ -561,6 +646,27 @@ std::string LineString::ToString() const { return AsEWKB(); } +template +double LineString::Distance(Point& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(line_, other.GetSpatialData()); +} + +template +double LineString::Distance(LineString& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(line_, other.GetSpatialData()); +} + +template +double LineString::Distance(Polygon& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(line_, other.GetSpatialData()); +} + template bool LineString::operator==(const LineString &other) { return AsEWKB() == other.AsEWKB(); @@ -647,6 +753,27 @@ std::string Polygon::AsEWKT() const { return ewkt; } +template +double Polygon::Distance(Point& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(polygon_, other.GetSpatialData()); +} + +template +double Polygon::Distance(LineString& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(polygon_, other.GetSpatialData()); +} + +template +double Polygon::Distance(Polygon& other) { + if (other.GetSrid() != GetSrid()) + THROW_CODE(InputError, "distance srid missmatch!"); + return bg::distance(polygon_, other.GetSpatialData()); +} + template std::string Polygon::ToString() const { return AsEWKB(); diff --git a/src/cypher/arithmetic/agg_funcs.h b/src/cypher/arithmetic/agg_funcs.h index 292113d11..3d546510f 100644 --- a/src/cypher/arithmetic/agg_funcs.h +++ b/src/cypher/arithmetic/agg_funcs.h @@ -179,6 +179,46 @@ struct FieldDataHash { return std::hash()(fd.AsString()); case FieldType::BLOB: return std::hash()(fd.AsBlob()); + case FieldType::POINT: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsPoint().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianPoint().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::LINESTRING: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsLineString().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianLineString().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::POLYGON: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsPolygon().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianPolygon().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::SPATIAL: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsSpatial().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianSpatial().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } default: throw std::runtime_error("Unhandled data type, probably corrupted data."); } diff --git a/src/cypher/arithmetic/arithmetic_expression.cpp b/src/cypher/arithmetic/arithmetic_expression.cpp index 969b98940..8f38b76d0 100644 --- a/src/cypher/arithmetic/arithmetic_expression.cpp +++ b/src/cypher/arithmetic/arithmetic_expression.cpp @@ -962,6 +962,277 @@ cypher::FieldData BuiltinFunction::Concat(RTContext *ctx, const Record &record, throw lgraph::CypherException("Function `CONCAT()` is not supported for: " + arg1.ToString()); } +cypher::FieldData BuiltinFunction::Point(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() == 1 || args.size() > 4) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() >= 2); + // Point(string) Returns a Point by parsing a string. + auto arg1 = args[1].Evaluate(ctx, record); + if (args.size() == 2) { + if (!arg1.IsString()) CYPHER_ARGUMENT_ERROR(); + auto pt = ::lgraph::FieldData::Point(arg1.constant.scalar.AsString()); + return cypher::FieldData(pt); + } + auto arg2 = args[2].Evaluate(ctx, record); + lgraph_api::SRID s; + double arg1_, arg2_; + if (arg1.IsInteger()) + arg1_ = static_cast(arg1.constant.scalar.AsInt64()); + else if (arg1.IsReal()) + arg1_ = arg1.constant.scalar.AsDouble(); + else + CYPHER_ARGUMENT_ERROR(); + + if (arg2.IsInteger()) + arg2_ = static_cast(arg2.constant.scalar.AsInt64()); + else if (arg2.IsReal()) + arg2_ = arg2.constant.scalar.AsDouble(); + else + CYPHER_ARGUMENT_ERROR(); + + // return point(2.32, 4.96) srid = 4326 by default; + if (args.size() == 3) { + s = lgraph_api::SRID::WGS84; + auto pt = ::lgraph::FieldData::Point(lgraph_api::Point + (arg1_, arg2_, s)); + return cypher::FieldData(pt); + } + + // return point(arg1, arg2, 7203); + auto arg3 = args[3].Evaluate(ctx, record); + if (!arg3.IsInteger()) + CYPHER_ARGUMENT_ERROR(); + + switch (arg3.constant.scalar.AsInt64()) { + case 4326: + s = lgraph_api::SRID::WGS84; + return cypher::FieldData(::lgraph::FieldData::Point + (lgraph_api::Point(arg1_, arg2_, s))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::Point + (lgraph_api::Point(arg1_, arg2_, s))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::PointWKB(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkb = args[1].Evaluate(ctx, record); + if (!wkb.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::POINT; + std::string wkb_ = wkb.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::Point(lgraph_api::Point + (s, t, 0, wkb_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::Point( + lgraph_api::Point(s, t, 0, wkb_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::Point( + lgraph_api::Point(s, t, 0, wkb_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::PointWKT(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkt = args[1].Evaluate(ctx, record); + if (!wkt.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::POINT; + std::string wkt_ = wkt.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::Point(lgraph_api::Point + (s, t, 1, wkt_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::Point( + lgraph_api::Point(s, t, 1, wkt_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::Point( + lgraph_api::Point(s, t, 1, wkt_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::LineString(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() != 2) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() == 2); + // LineString(string) Returns a LineString by parsing a string. + auto r = args[1].Evaluate(ctx, record); + if (!r.IsString()) CYPHER_ARGUMENT_ERROR(); + auto l = ::lgraph::FieldData::LineString(r.constant.scalar.AsString()); + return cypher::FieldData(l); +} + +cypher::FieldData BuiltinFunction::LineStringWKB(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkb = args[1].Evaluate(ctx, record); + if (!wkb.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::LINESTRING; + std::string wkb_ = wkb.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::LineString(lgraph_api::LineString + (s, t, 0, wkb_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::LineString( + lgraph_api::LineString + (s, t, 0, wkb_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::LineString( + lgraph_api::LineString + (s, t, 0, wkb_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::LineStringWKT(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkt = args[1].Evaluate(ctx, record); + if (!wkt.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::LINESTRING; + std::string wkt_ = wkt.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::LineString(lgraph_api::LineString + (s, t, 1, wkt_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::LineString( + lgraph_api::LineString + (s, t, 1, wkt_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::LineString( + lgraph_api::LineString + (s, t, 1, wkt_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::Polygon(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() != 2) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() == 2); + // Polygon(string) Returns a Point by parsing a string. + auto r = args[1].Evaluate(ctx, record); + if (!r.IsString()) CYPHER_ARGUMENT_ERROR(); + auto pl = ::lgraph::FieldData::Polygon(r.constant.scalar.AsString()); + return cypher::FieldData(pl); +} + +cypher::FieldData BuiltinFunction::PolygonWKB(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkb = args[1].Evaluate(ctx, record); + if (!wkb.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::POLYGON; + std::string wkb_ = wkb.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::Polygon(lgraph_api::Polygon + (s, t, 0, wkb_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::Polygon( + lgraph_api::Polygon + (s, t, 0, wkb_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::Polygon( + lgraph_api::Polygon + (s, t, 0, wkb_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + +cypher::FieldData BuiltinFunction::PolygonWKT(RTContext *ctx, const Record &record, + const std::vector &args) { + if (args.size() > 3) CYPHER_ARGUMENT_ERROR(); + CYPHER_THROW_ASSERT(args.size() <= 3); + auto wkt = args[1].Evaluate(ctx, record); + if (!wkt.IsString()) CYPHER_ARGUMENT_ERROR(); + + lgraph_api::SRID s = lgraph_api::SRID::WGS84; + lgraph_api::SpatialType t = lgraph_api::SpatialType::LINESTRING; + std::string wkt_ = wkt.constant.scalar.AsString(); + if (args.size() == 2) { + auto pt = ::lgraph::FieldData::Polygon(lgraph_api::Polygon + (s, t, 1, wkt_)); + return cypher::FieldData(pt); + } + + auto srid = args[2].Evaluate(ctx, record); + if (!srid.IsInteger()) CYPHER_ARGUMENT_ERROR(); + switch (srid.constant.scalar.AsInt64()) { + case 4326: + return cypher::FieldData(::lgraph::FieldData::Polygon( + lgraph_api::Polygon + (s, t, 1, wkt_))); + case 7203: + s = lgraph_api::SRID::CARTESIAN; + return cypher::FieldData(::lgraph::FieldData::Polygon( + lgraph_api::Polygon + (s, t, 1, wkt_))); + default: + CYPHER_ARGUMENT_ERROR(); + } +} + cypher::FieldData BuiltinFunction::Bin(RTContext *ctx, const Record &record, const std::vector &args) { if (args.size() != 2) CYPHER_ARGUMENT_ERROR(); diff --git a/src/cypher/arithmetic/arithmetic_expression.h b/src/cypher/arithmetic/arithmetic_expression.h index 6039312b1..f16c583a3 100644 --- a/src/cypher/arithmetic/arithmetic_expression.h +++ b/src/cypher/arithmetic/arithmetic_expression.h @@ -295,6 +295,54 @@ struct BuiltinFunction { static cypher::FieldData Mask(RTContext *ctx, const Record &record, const std::vector &args); + /* spatial functions */ + /** + * create point type data by point(double a, double b, srid(4326 in default)) + * or point(EWKB); + */ + static cypher::FieldData Point(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create point type data by pointwkb(wkb, srid(4326 in default)); + */ + static cypher::FieldData PointWKB(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create point type data by pointwkt(wkt, srid(4326 in default)); + */ + static cypher::FieldData PointWKT(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create linestring by LinieString(EKWB); + */ + static cypher::FieldData LineString(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create linestring by linestringwkb(wkb, srid(4326 in default)); + */ + static cypher::FieldData LineStringWKB(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create linestring by linestringwkt(wkt, srid(4326 in default)); + */ + static cypher::FieldData LineStringWKT(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create polygon by polygon(EWKB); + */ + static cypher::FieldData Polygon(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create polygon by polygonwkb(wkb, srid(4326 in default)); + */ + static cypher::FieldData PolygonWKB(RTContext *ctx, const Record &record, + const std::vector &args); + /** + * create polygon by polygonwkt(wkt, srid(4326 in default)); + */ + static cypher::FieldData PolygonWKT(RTContext *ctx, const Record &record, + const std::vector &args); + /* binary function (open cypher extension) */ static cypher::FieldData Bin(RTContext *ctx, const Record &record, const std::vector &args); @@ -492,6 +540,17 @@ struct ArithOpNode { ae_registered_funcs.emplace("mask", BuiltinFunction::Mask); ae_registered_funcs.emplace("bin", BuiltinFunction::Bin); ae_registered_funcs.emplace("coalesce", BuiltinFunction::Coalesce); + /* spatial functions */ + ae_registered_funcs.emplace("point", BuiltinFunction::Point); + ae_registered_funcs.emplace("pointwkb", BuiltinFunction::PointWKB); + ae_registered_funcs.emplace("pointwkt", BuiltinFunction::PointWKT); + ae_registered_funcs.emplace("linestring", BuiltinFunction::LineString); + ae_registered_funcs.emplace("linestringwkb", BuiltinFunction::LineStringWKB); + ae_registered_funcs.emplace("linestringwkt", BuiltinFunction::LineStringWKT); + ae_registered_funcs.emplace("polygon", BuiltinFunction::Polygon); + ae_registered_funcs.emplace("polygonwkb", BuiltinFunction::PolygonWKB); + ae_registered_funcs.emplace("polygonwkt", BuiltinFunction::PolygonWKT); + /* native API-like functions */ ae_registered_funcs.emplace("native.getedgefield", BuiltinFunction::NativeGetEdgeField); /* internal functions */ diff --git a/src/cypher/cypher_types.h b/src/cypher/cypher_types.h index 941a59aa0..f046f08a4 100644 --- a/src/cypher/cypher_types.h +++ b/src/cypher/cypher_types.h @@ -160,6 +160,17 @@ struct FieldData { bool IsString() const { return type == SCALAR && scalar.type == lgraph::FieldType::STRING; } + bool IsPoint() const { return type == SCALAR && scalar.type == lgraph::FieldType::POINT; } + + bool IsLineString() const { + return type == SCALAR && scalar.type == lgraph::FieldType::LINESTRING; + } + + bool IsPolygon() const { return type == SCALAR && scalar.type == lgraph::FieldType::POLYGON; } + + bool IsSpatial() const { return (IsPoint() || IsLineString() || IsPolygon()) || + (type == SCALAR && scalar.type == lgraph::FieldType::SPATIAL); } + bool IsArray() const { return type == ARRAY; } static FieldData Array(size_t n) { return FieldData(std::vector<::lgraph::FieldData>(n)); } diff --git a/src/cypher/filter/filter.h b/src/cypher/filter/filter.h index 7fb9e5599..74d44b513 100644 --- a/src/cypher/filter/filter.h +++ b/src/cypher/filter/filter.h @@ -60,6 +60,46 @@ struct FieldDataHash { return std::hash()(fd.AsString()); case FieldType::BLOB: return std::hash()(fd.AsBlob()); + case FieldType::POINT: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsPoint().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianPoint().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::LINESTRING: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsLineString().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianLineString().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::POLYGON: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsPolygon().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianPolygon().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } + case FieldType::SPATIAL: { + switch (fd.GetSRID()) { + case ::lgraph_api::SRID::WGS84: + return std::hash()(fd.AsWgsSpatial().AsEWKB()); + case ::lgraph_api::SRID::CARTESIAN: + return std::hash()(fd.AsCartesianSpatial().AsEWKB()); + default: + THROW_CODE(InputError, "unsupported spatial srid"); + } + } default: throw std::runtime_error("Unhandled data type, probably corrupted data."); } diff --git a/src/cypher/parser/cypher_base_visitor.h b/src/cypher/parser/cypher_base_visitor.h index 47fb932b4..8208c5f85 100644 --- a/src/cypher/parser/cypher_base_visitor.h +++ b/src/cypher/parser/cypher_base_visitor.h @@ -1824,6 +1824,7 @@ class CypherBaseVisitor : public LcypherVisitor { static const std::vector excluded_set = { "INT8", "INT16", "INT32", "INT64", "FLOAT", "DOUBLE", "STRING", "DATE", "DATETIME", "BLOB", "BOOL", + "POINT", "LINESTRING", "POLYGON", "SPATIAL" }; if (std::find_if(excluded_set.begin(), excluded_set.end(), [&var](const std::string &kw) { std::string upper_var(var.size(), ' '); diff --git a/src/cypher/procedure/procedure.cpp b/src/cypher/procedure/procedure.cpp index 7508de5e4..7b9cc7d20 100644 --- a/src/cypher/procedure/procedure.cpp +++ b/src/cypher/procedure/procedure.cpp @@ -2986,4 +2986,56 @@ void AlgoFunc::Jaccard(RTContext *ctx, const cypher::Record *record, const cyphe records->emplace_back(r.Snapshot()); } } + +void SpatialFunc::Distance(RTContext *ctx, const cypher::Record *record, + const cypher::VEC_EXPR &args, const cypher::VEC_STR &yield_items, + struct std::vector *records) { + CYPHER_ARG_CHECK(args.size() == 2, "wrong arguments number"); + + CYPHER_ARG_CHECK(args[0].type == parser::Expression::VARIABLE, + FMA_FMT("{} has to be a variable", + args[0].ToString())); + CYPHER_ARG_CHECK(args[1].type == parser::Expression::VARIABLE, + FMA_FMT("{} has to be a VARIABLE", + args[1].ToString())); + + auto s1 = record->symbol_table->symbols.find(args[0].String()); + auto s2 = record->symbol_table->symbols.find(args[1].String()); + if (s1 == record->symbol_table->symbols.end() || + s2 == record->symbol_table->symbols.end()) + CYPHER_TODO(); + auto &s1_ = record->values[s1->second.id]; + auto &s2_ = record->values[s2->second.id]; + CYPHER_THROW_ASSERT(s1_.IsSpatial() && s2_.IsSpatial()); + + ::lgraph_api::SRID srid1 = s1_.constant.scalar.GetSRID(); + ::lgraph_api::SRID srid2 = s2_.constant.scalar.GetSRID(); + CYPHER_THROW_ASSERT(srid1 == srid2); + double d = 0; + switch (srid1) { + case ::lgraph_api::SRID::WGS84: + { + auto Spatial1 = s1_.constant.scalar.AsWgsSpatial(); + auto Spatial2 = s2_.constant.scalar.AsWgsSpatial(); + d = Spatial1.Distance(Spatial2); + break; + } + + case ::lgraph_api::SRID::CARTESIAN: + { + auto Spatial1 = s1_.constant.scalar.AsCartesianSpatial(); + auto Spatial2 = s2_.constant.scalar.AsCartesianSpatial(); + d = Spatial1.Distance(Spatial2); + break; + } + + default: + throw std::runtime_error("unsupported srid type!"); + } + + Record r; + r.AddConstant(::lgraph::FieldData(d)); + records->emplace_back(r.Snapshot()); +} + } // namespace cypher diff --git a/src/cypher/procedure/procedure.h b/src/cypher/procedure/procedure.h index 2648d874c..f04defa19 100644 --- a/src/cypher/procedure/procedure.h +++ b/src/cypher/procedure/procedure.h @@ -373,6 +373,12 @@ class AlgoFunc { const VEC_STR &yield_items, std::vector *records); }; +class SpatialFunc { + public: + static void Distance(RTContext *ctx, const Record *record, const VEC_EXPR &args, + const VEC_STR &yield_items, std::vector *records); +}; + struct Procedure { /* > */ typedef std::vector>> SIG_SPEC; @@ -790,6 +796,12 @@ static std::vector global_procedures = { Procedure::SIG_SPEC{ {"similarity", {0, lgraph_api::LGraphType::FLOAT}}, }), + // spatial + Procedure("spatial.distance", SpatialFunc::Distance, + Procedure::SIG_SPEC{ + {"Spatial1", {0, lgraph_api::LGraphType::STRING}}, + {"Spatial2", {1, lgraph_api::LGraphType::STRING}}}, + Procedure::SIG_SPEC{{"distance", {0, lgraph_api::LGraphType::DOUBLE}}}), Procedure("dbms.security.listRoles", BuiltinProcedure::DbmsSecurityListRoles, Procedure::SIG_SPEC{}, diff --git a/src/cypher/resultset/record.h b/src/cypher/resultset/record.h index c1195ac33..c6b4e498c 100644 --- a/src/cypher/resultset/record.h +++ b/src/cypher/resultset/record.h @@ -98,6 +98,14 @@ struct Entry { bool IsNode() const { return type == NODE && node; } + bool IsPoint() const { return type == CONSTANT && constant.IsPoint(); } + + bool IsLineString() const { return type == CONSTANT && constant.IsLineString(); } + + bool IsPolygon() const { return type == CONSTANT && constant.IsPolygon(); } + + bool IsSpatial() const { return type == CONSTANT && constant.IsSpatial(); } + bool IsRelationship() const { return type == RELATIONSHIP && relationship; } bool operator==(const Entry &rhs) const { diff --git a/test/integration/test_ha_procedure.py b/test/integration/test_ha_procedure.py index 7dad79b7a..c69113f5d 100644 --- a/test/integration/test_ha_procedure.py +++ b/test/integration/test_ha_procedure.py @@ -503,7 +503,7 @@ def test_procedure(self): procedures = json.loads(ret[1]) #TODO when this assert failed , you should add the additional procedure test code or remove the deleted procedure test code log.info("procedures count : %s", len(procedures)) - assert len(procedures) == 93 + assert len(procedures) == 94 ha_client.logout() def test_graph(self): diff --git a/test/integration/test_procedure.py b/test/integration/test_procedure.py index 2577909a4..c72eb5caf 100644 --- a/test/integration/test_procedure.py +++ b/test/integration/test_procedure.py @@ -471,7 +471,7 @@ def test_procedure(self, server, client): procedures = json.loads(ret[1]) #TODO when this assert failed , you should add the additional procedure test code or remove the deleted procedure test code log.info("procedures count : %s", len(procedures)) - assert len(procedures) == 93 + assert len(procedures) == 94 @pytest.mark.parametrize("server", [SERVEROPT], indirect=True) diff --git a/test/test_cypher.cpp b/test/test_cypher.cpp index 2865a98f3..cb25e13cb 100644 --- a/test/test_cypher.cpp +++ b/test/test_cypher.cpp @@ -839,6 +839,29 @@ int test_expression(cypher::RTContext *ctx) { {"RETURN datetimeComponent(1582705717000, 'year'),datetimeComponent(1582705717000, " "'second'), datetimeComponent(1582705717000, 'microsecond')", 1}, + {"RETURN point('0101000020E6100000000000000000F03F0000000000000040') as p3", 1}, + {"RETURN linestring('0102000020231C000003000000000000000000000000000000000000000" + "00000000000004000000000000000400000000000000840000000000000F03F')", 1}, + {"RETURN polygon('0103000020E6100000010000000500000000000000000000000000000000" + "00000000000000000000000000000000001C400000000000001040000000000000" + "00400000000000000040000000000000000000000000000000000000000000000000')" + , 1}, + {"RETURN point(1.0, 2.0, 4326)", 1}, // (p1, p2), srid + {"RETURN point(3.0, 1.0, 7203)", 1}, + {"RETURN point(2.32, 4.96)", 1}, + {"WITH point(1, 1, 4326) AS p1, point(1, 1, 4326) AS p2 RETURN p1 = p2", 1}, + {"RETURN pointwkb('0101000000000000000000F03F0000000000000040'), 4326", 1}, + {"RETURN pointwkt('POINT(1.0 1.0)'), 7203", 1}, + {"RETURN pointwkb('0101000000000000000000F03F0000000000000040')", 1}, + {"RETURN pointwkt('POINT(1.0 1.0)')", 1}, + {"RETURN linestringwkb('01020000000300000000000000000000000000000000" + "000000000000000000004000000000000000400000000000000840000000000000F03F', 7203)", + 1}, + {"RETURN linestringwkt('LINESTRING(0 0,2 2,3 1)', 7203)", 1}, + {"RETURN polygonwkb('0103000000010000000500000000000000000000000000000000000000000" + "00000000000000000000000001C4000000000000010400000000000000040" + "0000000000000040000000000000000000000000000000000000000000000000', 4326)", 1}, + {"RETURN polygonwkt('POLYGON((0 0,0 7,4 2,2 0,0 0))', 7203)", 1}, }; std::vector scripts; std::vector check; @@ -2384,6 +2407,50 @@ void debug_stack_chaos(cypher::RTContext *ctx) { txn.Abort(); } +int test_spatial_procedure(cypher::RTContext *ctx) { + static const std::vector scripts_ = { + "CALL db.createVertexLabel('Location', 'name', 'name', STRING, false, 'geo', POINT, false)", + "CREATE (a_:Location {name:'A_', geo: POINT(1.0, 2.0)})", + "CREATE (b_:Location {name:'B_', geo: POINT(1.0, 2.0)})", + "CALL db.createVertexLabel" + "('Location_', 'name', 'name', STRING, false, 'geo1', LINESTRING, false," + "'geo2', POLYGON, false)", + "CREATE (c_:Location_ " + "{name:'C_', geo1: LINESTRING('0102000020E6100000030000000000000000000000000" + "0000000000000000000000000004000000000000000400000000000000840000000000000F03F'), " + "geo2: polygonwkt('POLYGON((0 0,0 7,4 2,2 0,0 0))')})" + }; + eval_scripts(ctx, scripts_); + + static const std::vector> script_check = { + {"with point(2.0, 2.0, 7203) as p1, point(2.0, 1.0, 7203) as p2\n" + "CALL spatial.distance(p1, p2) YIELD distance RETURN distance = 1", 1}, + {"with LineStringWKB('01020000000300000000000000000000000000000000" + "000000000000000000004000000000000000400000000000000840000000000000F03F') " + "as linestring, " + "PolygonWKT('POLYGON((0 0,0 7,4 2,2 0,0 0))', 4326) as polygon\n" + "CALL spatial.distance(linestring, polygon) YIELD distance RETURN distance = 0", 1}, + {"MATCH (l1:Location {name:'A_'}), (l2:Location {name:'B_'}) with\n" + "l1.geo as g1, l2.geo as g2\n" + "CALL spatial.distance(g1, g2) YIELD distance RETURN distance = 0", 1}, + {"MATCH (l2:Location {name:'B_'}), (l3:Location_ {name:'C_'}) with\n" + "l2.geo as g2, l3.geo2 as g3\n" + "CALL spatial.distance(g2, g3) YIELD distance RETURN distance = 0", 1}, + {"MATCH (l1:Location_ {name:'C_'}) with\n" + "l1.geo1 as g1, l1.geo2 as g2\n" + "CALL spatial.distance(g1, g2) YIELD distance RETURN distance = 0", 1}, + }; + std::vector scripts; + std::vector check; + for (auto &s : script_check) { + scripts.emplace_back(s.first); + check.emplace_back(s.second); + } + eval_scripts_check(ctx, scripts, check); + + return 0; +} + enum TestCase { TC_FILE_SCRIPT = 1, TC_INTERACTIVE = 2, @@ -2417,6 +2484,7 @@ enum TestCase { TC_AGGREGATE, TC_ALGO, TC_TOPN, + TC_SPATIAL_PROCEDURE, TC_ERROR_REPORT = 201, TC_DEBUG_STACK_CHAOS, TC_LDBC_SNB = 301, @@ -2451,16 +2519,17 @@ TEST_P(TestCypher, Cypher) { "{}-list-comprehension" " {}-profile; {}-unwind; {}-procedure; {}-add; {}-set; {}-del; {}-remove; {}-order by; " "{}-merge;" - " {}-create yago; {}-aggregate; {}-algo; {}-topn; {}-error report; {}-snb; " - "{}-optimization; {}-fix_crash_issues;" + " {}-create yago; {}-aggregate; {}-algo; {}-topn; {}-spatial_procedure; " + "{}-error report; {}-snb; {}-optimization; {}-fix_crash_issues;" " {}-undefined_variable; {}-create_label; {}-determine_read_only; {}-edge_id_query;" " {}-empty_graph;", TC_FILE_SCRIPT, TC_INTERACTIVE, TC_FIND, TC_QUERY, TC_HINT, TC_MULTI_MATCH, TC_OPTIONAL_MATCH, TC_UNION, TC_FUNCTION, TC_PARAMETER, TC_VAR_LEN_EDGE, TC_UNIQUENESS, TC_FUNC_FILTER, TC_EXPRESSION, TC_WITH, TC_LIST_COMPREHENSION, TC_PROFILE, TC_UNWIND, TC_PROCEDURE, TC_ADD, TC_SET, TC_DELETE, TC_REMOVE, TC_ORDER_BY, TC_MERGE, TC_CREATE_YAGO, - TC_AGGREGATE, TC_ALGO, TC_TOPN, TC_ERROR_REPORT, TC_LDBC_SNB, TC_OPT, TC_FIX_CRASH_ISSUES, - TC_UNDEFINED_VAR, TC_CREATE_LABEL, TC_READONLY, TC_EDGE_ID, TC_EMPTY_GRAPH); + TC_AGGREGATE, TC_ALGO, TC_TOPN, TC_SPATIAL_PROCEDURE, TC_ERROR_REPORT, TC_LDBC_SNB, TC_OPT, + TC_FIX_CRASH_ISSUES, TC_UNDEFINED_VAR, TC_CREATE_LABEL, TC_READONLY, TC_EDGE_ID, + TC_EMPTY_GRAPH); test_case = GetParam().tc; database = GetParam().d; int argc = _ut_argc; @@ -2577,6 +2646,9 @@ TEST_P(TestCypher, Cypher) { case TC_TOPN: test_topn(&db); break; + case TC_SPATIAL_PROCEDURE: + test_spatial_procedure(&db); + break; case TC_MERGE: test_merge(&db); break; @@ -2628,6 +2700,6 @@ INSTANTIATE_TEST_CASE_P( ParamCypher{15, 1}, ParamCypher{16, 1}, ParamCypher{18, 1}, ParamCypher{101, 1}, ParamCypher{102, 1}, ParamCypher{103, 1}, ParamCypher{104, 2}, ParamCypher{105, 2}, ParamCypher{106, 1}, ParamCypher{107, 1}, ParamCypher{108, 2}, ParamCypher{109, 2}, - ParamCypher{110, 2}, ParamCypher{111, 2}, ParamCypher{112, 1}, ParamCypher{301, 2}, - ParamCypher{401, 1}, ParamCypher{402, 1}, ParamCypher{403, 1}, ParamCypher{404, 2}, - ParamCypher{500, 0}, ParamCypher{501, 1})); + ParamCypher{110, 2}, ParamCypher{111, 2}, ParamCypher{112, 1}, ParamCypher{113, 1}, + ParamCypher{301, 2}, ParamCypher{401, 1}, ParamCypher{402, 1}, ParamCypher{403, 1}, + ParamCypher{404, 2}, ParamCypher{500, 0}, ParamCypher{501, 1}));