diff --git a/be/src/exprs/function/geo/functions_geo.cpp b/be/src/exprs/function/geo/functions_geo.cpp index b4967780d2c8dc..8b05644e0b890e 100644 --- a/be/src/exprs/function/geo/functions_geo.cpp +++ b/be/src/exprs/function/geo/functions_geo.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "common/compiler_util.h" @@ -196,6 +197,166 @@ struct StY { } }; +// Bounding box accessors, Trino compatible: +// st_xmax / st_xmin / st_ymax / st_ymin return the max/min X (longitude) and +// Y (latitude) of a geometry. All shape types are decoded via GeoShape, and the +// result is NULL when the value is not available. +struct StXMax { + static constexpr auto NAME = "st_xmax"; + static const size_t NUM_ARGS = 1; + using Type = DataTypeFloat64; + static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { + DCHECK_EQ(arguments.size(), 1); + auto& input = block.get_by_position(arguments[0]).column; + + auto size = input->size(); + + auto res = ColumnFloat64::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); + res->reserve(size); + + for (int row = 0; row < size; ++row) { + auto value = input->get_data_at(row); + auto shape = GeoShape::from_encoded(value.data, value.size); + + if (shape == nullptr) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + const double x_max = shape->bounding_box().x_max; + if (std::isnan(x_max)) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + res->insert_value(x_max); + } + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + + return Status::OK(); + } +}; + +struct StXMin { + static constexpr auto NAME = "st_xmin"; + static const size_t NUM_ARGS = 1; + using Type = DataTypeFloat64; + static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { + DCHECK_EQ(arguments.size(), 1); + auto& input = block.get_by_position(arguments[0]).column; + + auto size = input->size(); + + auto res = ColumnFloat64::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); + res->reserve(size); + + for (int row = 0; row < size; ++row) { + auto value = input->get_data_at(row); + auto shape = GeoShape::from_encoded(value.data, value.size); + + if (shape == nullptr) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + const double x_min = shape->bounding_box().x_min; + if (std::isnan(x_min)) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + res->insert_value(x_min); + } + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + + return Status::OK(); + } +}; + +struct StYMax { + static constexpr auto NAME = "st_ymax"; + static const size_t NUM_ARGS = 1; + using Type = DataTypeFloat64; + static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { + DCHECK_EQ(arguments.size(), 1); + auto& input = block.get_by_position(arguments[0]).column; + + auto size = input->size(); + + auto res = ColumnFloat64::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); + res->reserve(size); + + for (int row = 0; row < size; ++row) { + auto value = input->get_data_at(row); + auto shape = GeoShape::from_encoded(value.data, value.size); + + if (shape == nullptr) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + const double y_max = shape->bounding_box().y_max; + if (std::isnan(y_max)) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + res->insert_value(y_max); + } + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + + return Status::OK(); + } +}; + +struct StYMin { + static constexpr auto NAME = "st_ymin"; + static const size_t NUM_ARGS = 1; + using Type = DataTypeFloat64; + static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { + DCHECK_EQ(arguments.size(), 1); + auto& input = block.get_by_position(arguments[0]).column; + + auto size = input->size(); + + auto res = ColumnFloat64::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); + res->reserve(size); + + for (int row = 0; row < size; ++row) { + auto value = input->get_data_at(row); + auto shape = GeoShape::from_encoded(value.data, value.size); + + if (shape == nullptr) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + const double y_min = shape->bounding_box().y_min; + if (std::isnan(y_min)) { + null_map_data[row] = 1; + res->insert_default(); + continue; + } + res->insert_value(y_min); + } + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + + return Status::OK(); + } +}; + struct StDistanceSphere { static constexpr auto NAME = "st_distance_sphere"; static const size_t NUM_ARGS = 4; @@ -1084,6 +1245,10 @@ void register_function_geo(SimpleFunctionFactory& factory) { factory.register_function>>(); factory.register_function>(); factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); factory.register_function>(); factory.register_function>(); factory.register_function>(); diff --git a/be/src/exprs/function/geo/geo_types.cpp b/be/src/exprs/function/geo/geo_types.cpp index 6a48a3dc36e819..aed4c53920e4b7 100644 --- a/be/src/exprs/function/geo/geo_types.cpp +++ b/be/src/exprs/function/geo/geo_types.cpp @@ -37,6 +37,7 @@ #include "core/assert_cast.h" // IWYU pragma: no_include +#include #include #include #include @@ -53,6 +54,31 @@ namespace doris { constexpr double TOLERANCE = 1e-6; +namespace { + +// NaN-safe min/max, used to merge bounding boxes of multiple geometries. +double nan_min(double a, double b) { + if (std::isnan(a)) { + return b; + } + if (std::isnan(b)) { + return a; + } + return std::min(a, b); +} + +double nan_max(double a, double b) { + if (std::isnan(a)) { + return b; + } + if (std::isnan(b)) { + return a; + } + return std::max(a, b); +} + +} // namespace + GeoPoint::GeoPoint() : _point(new S2Point()) {} GeoPoint::~GeoPoint() = default; @@ -613,6 +639,11 @@ double GeoPoint::y() const { return std::stod(absl::StrFormat("%.13f", S2LatLng::Latitude(*_point).degrees())); } +BoundingBox GeoPoint::bounding_box() const { + // A point degenerates to a box with zero width and height. + return {.x_max = x(), .x_min = x(), .y_max = y(), .y_min = y()}; +} + std::string GeoPoint::as_wkt() const { std::stringstream ss; ss << "POINT ("; @@ -787,6 +818,20 @@ const S2Point* GeoLine::getPoint(int i) const { return &(_polyline->vertex(i)); } +BoundingBox GeoLine::bounding_box() const { + BoundingBox box; + for (int i = 0; i < numPoint(); ++i) { + const S2Point& p = *getPoint(i); + const double lon = S2LatLng::Longitude(p).degrees(); + const double lat = S2LatLng::Latitude(p).degrees(); + box.x_max = nan_max(box.x_max, lon); + box.x_min = nan_min(box.x_min, lon); + box.y_max = nan_max(box.y_max, lat); + box.y_min = nan_min(box.y_min, lat); + } + return box; +} + GeoParseStatus GeoPolygon::from_coords(const GeoCoordinateListList& list) { return to_s2polygon(list, &_polygon); } @@ -1109,6 +1154,23 @@ S2Loop* GeoPolygon::getLoop(int i) const { return _polygon->loop(i); } +BoundingBox GeoPolygon::bounding_box() const { + BoundingBox box; + for (int loop_idx = 0; loop_idx < numLoops(); ++loop_idx) { + S2Loop* loop = getLoop(loop_idx); + for (int i = 0; i < loop->num_vertices(); ++i) { + const S2Point& p = loop->vertex(i); + const double lon = S2LatLng::Longitude(p).degrees(); + const double lat = S2LatLng::Latitude(p).degrees(); + box.x_max = nan_max(box.x_max, lon); + box.x_min = nan_min(box.x_min, lon); + box.y_max = nan_max(box.y_max, lat); + box.y_min = nan_min(box.y_min, lat); + } + } + return box; +} + GeoParseStatus GeoMultiPolygon::from_coords(const std::vector& list) { _polygons.clear(); for (const auto& coords_list : list) { @@ -1745,6 +1807,18 @@ double GeoMultiPolygon::Length() const { return total_length; } +BoundingBox GeoMultiPolygon::bounding_box() const { + BoundingBox box; + for (const auto& polygon : _polygons) { + BoundingBox sub_box = polygon->bounding_box(); + box.x_max = nan_max(box.x_max, sub_box.x_max); + box.x_min = nan_min(box.x_min, sub_box.x_min); + box.y_max = nan_max(box.y_max, sub_box.y_max); + box.y_min = nan_min(box.y_min, sub_box.y_min); + } + return box; +} + double GeoCircle::Length() const { // GeoCircle is always valid (guaranteed by constructor) // Get the radius in meters @@ -1754,6 +1828,15 @@ double GeoCircle::Length() const { return 2.0 * M_PI * radius_meters; } +BoundingBox GeoCircle::bounding_box() const { + // Treat the circle as its center point (radius is in meters, so the exact + // bounding box depends on the projection; keep it simple like a point). + const S2Point& center = _cap->center(); + const double lon = S2LatLng::Longitude(center).degrees(); + const double lat = S2LatLng::Latitude(center).degrees(); + return {.x_max = lon, .x_min = lon, .y_max = lat, .y_min = lat}; +} + double GeoPoint::Distance(const GeoShape* rhs) const { // rhs is guaranteed to be valid by StDistance (functions_geo.cpp) switch (rhs->type()) { diff --git a/be/src/exprs/function/geo/geo_types.h b/be/src/exprs/function/geo/geo_types.h index 146ebf2a847603..472ecb4dd5aa62 100644 --- a/be/src/exprs/function/geo/geo_types.h +++ b/be/src/exprs/function/geo/geo_types.h @@ -19,6 +19,7 @@ #include +#include #include #include #include @@ -40,6 +41,15 @@ using S2Point = Vector3_d; namespace doris { +// Bounding box of a geometry, backing the Trino compatible functions +// ST_XMax / ST_XMin / ST_YMax / ST_YMin. Values are NaN when not available. +struct BoundingBox { + double x_max = std::numeric_limits::quiet_NaN(); + double x_min = std::numeric_limits::quiet_NaN(); + double y_max = std::numeric_limits::quiet_NaN(); + double y_min = std::numeric_limits::quiet_NaN(); +}; + class GeoShape { public: virtual ~GeoShape() = default; @@ -84,6 +94,10 @@ class GeoShape { virtual int num_geometries() const { return 1; } virtual int num_points() const { return -1; } + // Bounding box of the shape. Returns an all-NaN box for shape types that do + // not support the accessor. + virtual BoundingBox bounding_box() const { return {}; } + protected: virtual void encode(std::string* buf) = 0; virtual bool decode(const void* data, size_t size) = 0; @@ -127,6 +141,7 @@ class GeoPoint : public GeoShape { double x() const; double y() const; + BoundingBox bounding_box() const override; int num_geometries() const override { return 1; } int num_points() const override { return 1; } @@ -166,6 +181,7 @@ class GeoLine : public GeoShape { int numPoint() const; const S2Point* getPoint(int i) const; + BoundingBox bounding_box() const override; int num_geometries() const override { return 1; } int num_points() const override { return numPoint(); } @@ -206,6 +222,7 @@ class GeoPolygon : public GeoShape { double getArea() const; double Length() const override; double Distance(const GeoShape* rhs) const override; + BoundingBox bounding_box() const override; S2Loop* getLoop(int i) const; int num_geometries() const override { return 1; } @@ -243,6 +260,7 @@ class GeoMultiPolygon : public GeoShape { double getArea() const; double Length() const override; double Distance(const GeoShape* rhs) const override; + BoundingBox bounding_box() const override; int num_geometries() const override { return static_cast(_polygons.size()); } int num_points() const override; @@ -278,6 +296,7 @@ class GeoCircle : public GeoShape { double getArea() const; double Length() const override; double Distance(const GeoShape* rhs) const override; + BoundingBox bounding_box() const override; protected: void encode(std::string* buf) override; diff --git a/be/test/exprs/function/geo/geo_types_test.cpp b/be/test/exprs/function/geo/geo_types_test.cpp index 580218f4058e71..d01bf658354d2d 100644 --- a/be/test/exprs/function/geo/geo_types_test.cpp +++ b/be/test/exprs/function/geo/geo_types_test.cpp @@ -65,6 +65,41 @@ TEST_F(GeoTypesTest, point_normal) { } } +TEST_F(GeoTypesTest, bounding_box_point) { + GeoPoint point; + auto status = point.from_coord(116.123, 63.546); + EXPECT_EQ(GEO_PARSE_OK, status); + auto box = point.bounding_box(); + EXPECT_DOUBLE_EQ(116.123, box.x_max); + EXPECT_DOUBLE_EQ(116.123, box.x_min); + EXPECT_DOUBLE_EQ(63.546, box.y_max); + EXPECT_DOUBLE_EQ(63.546, box.y_min); +} + +TEST_F(GeoTypesTest, bounding_box_linestring) { + const char* wkt = "LINESTRING (30 10, 10 30, 40 40)"; + GeoParseStatus status; + auto line = GeoShape::from_wkt(wkt, strlen(wkt), status); + EXPECT_NE(nullptr, line.get()); + auto box = line->bounding_box(); + EXPECT_NEAR(40.0, box.x_max, 1e-9); + EXPECT_NEAR(10.0, box.x_min, 1e-9); + EXPECT_NEAR(40.0, box.y_max, 1e-9); + EXPECT_NEAR(10.0, box.y_min, 1e-9); +} + +TEST_F(GeoTypesTest, bounding_box_polygon) { + const char* wkt = "POLYGON ((0 0, 4 0, 4 3, 0 3, 0 0))"; + GeoParseStatus status; + auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status); + EXPECT_NE(nullptr, polygon.get()); + auto box = polygon->bounding_box(); + EXPECT_NEAR(4.0, box.x_max, 1e-9); + EXPECT_NEAR(0.0, box.x_min, 1e-9); + EXPECT_NEAR(3.0, box.y_max, 1e-9); + EXPECT_NEAR(0.0, box.y_min, 1e-9); +} + TEST_F(GeoTypesTest, point_invalid) { GeoPoint point; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java index 762f3afc0f6649..64d6bce4e30cd5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java @@ -500,7 +500,11 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygonfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StTouches; import org.apache.doris.nereids.trees.expressions.functions.scalar.StX; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StXMax; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StXMin; import org.apache.doris.nereids.trees.expressions.functions.scalar.StY; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StYMax; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StYMin; import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap; @@ -1090,7 +1094,11 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(StPolygon.class, "st_polygon"), scalar(StPolygonfromtext.class, "st_polygonfromtext"), scalar(StX.class, "st_x"), + scalar(StXMax.class, "st_xmax"), + scalar(StXMin.class, "st_xmin"), scalar(StY.class, "st_y"), + scalar(StYMax.class, "st_ymax"), + scalar(StYMin.class, "st_ymin"), scalar(StartsWith.class, "starts_with"), scalar(Strcmp.class, "strcmp"), scalar(StripNullValue.class, "strip_null_value"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMax.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMax.java new file mode 100644 index 00000000000000..141436f6f6e89a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMax.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'st_xmax'. This class is generated by GenerateFunction. + */ +public class StXMax extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(DoubleType.INSTANCE).args(StringType.INSTANCE) + ); + + /** + * constructor with 1 argument. + */ + public StXMax(Expression arg) { + super("st_xmax", arg); + } + + /** constructor for withChildren and reuse signature */ + private StXMax(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public StXMax withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new StXMax(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStXMax(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMin.java new file mode 100644 index 00000000000000..d078405c261575 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StXMin.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'st_xmin'. This class is generated by GenerateFunction. + */ +public class StXMin extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(DoubleType.INSTANCE).args(StringType.INSTANCE) + ); + + /** + * constructor with 1 argument. + */ + public StXMin(Expression arg) { + super("st_xmin", arg); + } + + /** constructor for withChildren and reuse signature */ + private StXMin(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public StXMin withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new StXMin(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStXMin(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMax.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMax.java new file mode 100644 index 00000000000000..cbe6ac31ecf793 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMax.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'st_ymax'. This class is generated by GenerateFunction. + */ +public class StYMax extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(DoubleType.INSTANCE).args(StringType.INSTANCE) + ); + + /** + * constructor with 1 argument. + */ + public StYMax(Expression arg) { + super("st_ymax", arg); + } + + /** constructor for withChildren and reuse signature */ + private StYMax(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public StYMax withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new StYMax(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStYMax(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMin.java new file mode 100644 index 00000000000000..0abe2bf2f0b414 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StYMin.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'st_ymin'. This class is generated by GenerateFunction. + */ +public class StYMin extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(DoubleType.INSTANCE).args(StringType.INSTANCE) + ); + + /** + * constructor with 1 argument. + */ + public StYMin(Expression arg) { + super("st_ymin", arg); + } + + /** constructor for withChildren and reuse signature */ + private StYMin(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public StYMin withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new StYMin(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStYMin(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java index 6b73a00b85440f..b407fb2db867d8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java @@ -519,7 +519,11 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygonfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StTouches; import org.apache.doris.nereids.trees.expressions.functions.scalar.StX; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StXMax; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StXMin; import org.apache.doris.nereids.trees.expressions.functions.scalar.StY; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StYMax; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StYMin; import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap; @@ -2468,6 +2472,22 @@ default R visitStPolygonfromtext(StPolygonfromtext stPolygonfromtext, C context) return visitScalarFunction(stPolygonfromtext, context); } + default R visitStXMax(StXMax stXMax, C context) { + return visitScalarFunction(stXMax, context); + } + + default R visitStXMin(StXMin stXMin, C context) { + return visitScalarFunction(stXMin, context); + } + + default R visitStYMax(StYMax stYMax, C context) { + return visitScalarFunction(stYMax, context); + } + + default R visitStYMin(StYMin stYMin, C context) { + return visitScalarFunction(stYMin, context); + } + default R visitStX(StX stX, C context) { return visitScalarFunction(stX, context); } diff --git a/regression-test/data/nereids_function_p0/scalar_function/st_bounding_box.out b/regression-test/data/nereids_function_p0/scalar_function/st_bounding_box.out new file mode 100644 index 00000000000000..ea1202a9c60a6c --- /dev/null +++ b/regression-test/data/nereids_function_p0/scalar_function/st_bounding_box.out @@ -0,0 +1,159 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !sql_st_xmax_Varchar -- +\N +-74.35620117 +-46.35620117 +5 +16.35620117 +43.35620117 +47.35620117 +90.35620117 +90.35620117 +90.35620117 +90.35620117 +98.35620117 +126.35620117 + +-- !sql_st_xmax_Varchar_notnull -- +-74.35620117 +-46.35620117 +5 +16.35620117 +43.35620117 +47.35620117 +90.35620117 +90.35620117 +90.35620117 +90.35620117 +98.35620117 +126.35620117 + +-- !sql_st_xmin_Varchar -- +\N +-74.35620117 +-46.35620117 +5 +16.35620117 +43.35620117 +47.35620117 +90.35620117 +90.35620117 +90.35620117 +90.35620117 +98.35620117 +126.35620117 + +-- !sql_st_xmin_Varchar_notnull -- +-74.35620117 +-46.35620117 +5 +16.35620117 +43.35620117 +47.35620117 +90.35620117 +90.35620117 +90.35620117 +90.35620117 +98.35620117 +126.35620117 + +-- !sql_st_ymax_Varchar -- +\N +-39.939093 +5 +19.939093 +26.939093 +35.939093 +36.939093 +39.939093 +39.939093 +47.939093 +49.939093 +59.939093 +79.939093 + +-- !sql_st_ymax_Varchar_notnull -- +-39.939093 +5 +19.939093 +26.939093 +35.939093 +36.939093 +39.939093 +39.939093 +47.939093 +49.939093 +59.939093 +79.939093 + +-- !sql_st_ymin_Varchar -- +\N +-39.939093 +5 +19.939093 +26.939093 +35.939093 +36.939093 +39.939093 +39.939093 +47.939093 +49.939093 +59.939093 +79.939093 + +-- !sql_st_ymin_Varchar_notnull -- +-39.939093 +5 +19.939093 +26.939093 +35.939093 +36.939093 +39.939093 +39.939093 +47.939093 +49.939093 +59.939093 +79.939093 + +-- !sql_st_xmax_polygon -- +\N +4.000000000000001 +9.999999999999998 +9.999999999999998 +10 +12 +16 +34.00000000000001 +38.00000000000001 +42 +45 +48.00000000000001 +67 + +-- !sql_st_ymin_polygon -- +\N +0 +0 +0 +0 +0 +0 +0.9999999999999998 +1 +1 +1 +1 +4 + +-- !sql_st_xmax_linestring -- +3.0000000000000004 + +-- !sql_st_ymin_linestring -- +1 + +-- !sql_st_xmax_invalid -- +\N + +-- !sql_st_xmax_null -- +\N + diff --git a/regression-test/suites/nereids_function_p0/scalar_function/st_bounding_box.groovy b/regression-test/suites/nereids_function_p0/scalar_function/st_bounding_box.groovy new file mode 100644 index 00000000000000..94faba5d1eff66 --- /dev/null +++ b/regression-test/suites/nereids_function_p0/scalar_function/st_bounding_box.groovy @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Bounding box accessor functions st_xmax/st_xmin/st_ymax/st_ymin +// (Trino compatible), implemented on top of GeoShape::bounding_box. +suite("nereids_scalar_fn_st_bounding_box") { + sql 'use regression_test_nereids_function_p0' + sql 'set enable_nereids_planner=true' + sql 'set enable_fallback_to_original_planner=false' + qt_sql_st_xmax_Varchar "select st_xmax(st_point(x_lng, x_lat)) from fn_test order by 1" + qt_sql_st_xmax_Varchar_notnull "select st_xmax(st_point(x_lng, x_lat)) from fn_test_not_nullable order by 1" + qt_sql_st_xmin_Varchar "select st_xmin(st_point(x_lng, x_lat)) from fn_test order by 1" + qt_sql_st_xmin_Varchar_notnull "select st_xmin(st_point(x_lng, x_lat)) from fn_test_not_nullable order by 1" + qt_sql_st_ymax_Varchar "select st_ymax(st_point(x_lng, x_lat)) from fn_test order by 1" + qt_sql_st_ymax_Varchar_notnull "select st_ymax(st_point(x_lng, x_lat)) from fn_test_not_nullable order by 1" + qt_sql_st_ymin_Varchar "select st_ymin(st_point(x_lng, x_lat)) from fn_test order by 1" + qt_sql_st_ymin_Varchar_notnull "select st_ymin(st_point(x_lng, x_lat)) from fn_test_not_nullable order by 1" + qt_sql_st_xmax_polygon "select st_xmax(st_polygon(polygon_wkt)) from fn_test order by 1" + qt_sql_st_ymin_polygon "select st_ymin(st_polygon(polygon_wkt)) from fn_test order by 1" + qt_sql_st_xmax_linestring "select st_xmax(st_linestringfromtext('LINESTRING (1 1, 3 2, 2 4)'))" + qt_sql_st_ymin_linestring "select st_ymin(st_linestringfromtext('LINESTRING (1 1, 3 2, 2 4)'))" + qt_sql_st_xmax_invalid "select st_xmax('not a geometry')" + qt_sql_st_xmax_null "select st_xmax(NULL)" +}