Skip to content

Commit

Permalink
added st_makeenvelope builtin
Browse files Browse the repository at this point in the history
* fixes cockroachdb#80357

Release note (sql change): added builtin st_makeenvelope

Signed-off-by: Frédéric BIDON <[email protected]>
  • Loading branch information
fredbi committed Apr 22, 2022
1 parent 2f8938f commit 35f2510
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 2 deletions.
4 changes: 4 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,10 @@ calculated, the result is transformed back into a Geography with SRID 4326.</p>
</span></td></tr>
<tr><td><a name="st_makebox2d"></a><code>st_makebox2d(geometry_a: geometry, geometry_b: geometry) &rarr; box2d</code></td><td><span class="funcdesc"><p>Creates a box2d from two points. Errors if arguments are not two non-empty points.</p>
</span></td></tr>
<tr><td><a name="st_makeenvelope"></a><code>st_makeenvelope(xmin: <a href="float.html">float</a>, ymin: <a href="float.html">float</a>, xmax: <a href="float.html">float</a>, ymax: <a href="float.html">float</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a box2d from bounds (the default srid value is 0).</p>
</span></td></tr>
<tr><td><a name="st_makeenvelope"></a><code>st_makeenvelope(xmin: <a href="float.html">float</a>, ymin: <a href="float.html">float</a>, xmax: <a href="float.html">float</a>, ymax: <a href="float.html">float</a>, srid: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a box2d from bounds.</p>
</span></td></tr>
<tr><td><a name="st_makepoint"></a><code>st_makepoint(x: <a href="float.html">float</a>, y: <a href="float.html">float</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a new Point with the given X and Y coordinates.</p>
</span></td></tr>
<tr><td><a name="st_makepoint"></a><code>st_makepoint(x: <a href="float.html">float</a>, y: <a href="float.html">float</a>, z: <a href="float.html">float</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a new Point with the given X, Y, and Z coordinates.</p>
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/sem/builtins/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ go_test(
embed = [":builtins"],
deps = [
"//pkg/base",
"//pkg/geo/geopb",
"//pkg/keys",
"//pkg/kv",
"//pkg/security",
Expand Down
65 changes: 63 additions & 2 deletions pkg/sql/sem/builtins/geo_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -5302,7 +5302,7 @@ The calculations are done on a sphere.`,
},
Info: infoBuilder{
info: `Snaps the vertices and segments of input geometry the target geometry's vertices.
Tolerance is used to control where snapping is performed. The result geometry is the input geometry with the vertices snapped.
Tolerance is used to control where snapping is performed. The result geometry is the input geometry with the vertices snapped.
If no snapping occurs then the input geometry is returned unchanged.`,
}.String(),
Volatility: tree.VolatilityImmutable,
Expand Down Expand Up @@ -5558,6 +5558,42 @@ Bottom Left.`,
Volatility: tree.VolatilityImmutable,
},
),
"st_makeenvelope": makeBuiltin(
defProps(),
tree.Overload{
Types: tree.ArgTypes{
{"xmin", types.Float},
{"ymin", types.Float},
{"xmax", types.Float},
{"ymax", types.Float},
{"srid", types.Int},
},
ReturnType: tree.FixedReturnType(types.Geometry),
Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
return makeGeometryFromBounds(args)
},
Info: infoBuilder{
info: "Returns a box2d from bounds.",
}.String(),
Volatility: tree.VolatilityImmutable,
},
tree.Overload{
Types: tree.ArgTypes{
{"xmin", types.Float},
{"ymin", types.Float},
{"xmax", types.Float},
{"ymax", types.Float},
},
ReturnType: tree.FixedReturnType(types.Geometry),
Fn: func(_ *tree.EvalContext, args tree.Datums) (tree.Datum, error) {
return makeGeometryFromBounds(args)
},
Info: infoBuilder{
info: "Returns a box2d from bounds (the default srid value is 0).",
}.String(),
Volatility: tree.VolatilityImmutable,
},
),
"st_flipcoordinates": makeBuiltin(
defProps(),
tree.Overload{
Expand Down Expand Up @@ -6768,7 +6804,7 @@ May return a Point or LineString in the case of degenerate inputs.`,
return tree.NewDInt(tree.DInt(ret)), nil
},
Info: infoBuilder{
info: `Returns an interger value defining behavior of crossing of lines:
info: `Returns an interger value defining behavior of crossing of lines:
0: lines do not cross,
-1: linestring_b crosses linestring_a from right to left,
1: linestring_b crosses linestring_a from left to right,
Expand Down Expand Up @@ -7502,3 +7538,28 @@ func applyGeoindexConfigStorageParams(
}
return indexDesc.GeoConfig, nil
}

// makeGeometryFromBounds builds a rectangle geometry from types.Float bounds as datums
func makeGeometryFromBounds(args tree.Datums) (tree.Datum, error) {
xmin := float64(tree.MustBeDFloat(args[0]))
ymin := float64(tree.MustBeDFloat(args[1]))
xmax := float64(tree.MustBeDFloat(args[2]))
ymax := float64(tree.MustBeDFloat(args[3]))

var srid int
if len(args) > 4 {
srid = int(tree.MustBeDInt(args[4]))
}

extent, err := geo.MakeGeometryFromGeomT(
geom.NewBounds(geom.XY).
Set(xmin, ymin, xmax, ymax).
Polygon().
SetSRID(srid),
)
if err != nil {
return nil, err
}

return tree.NewDGeometry(extent), nil
}
86 changes: 86 additions & 0 deletions pkg/sql/sem/builtins/geo_builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"testing"
"unicode"

"github.com/cockroachdb/cockroach/pkg/geo/geopb"
"github.com/cockroachdb/cockroach/pkg/sql/randgen"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/types"
Expand Down Expand Up @@ -103,3 +104,88 @@ func TestGeoBuiltinsPointEmptyArgs(t *testing.T) {
})
}
}

func mustParseDFloat(t testing.TB, in string) tree.Datum {
d, err := tree.ParseDFloat(in)
require.NoError(t, err)

return d
}

func mustParseDGeometry(t testing.TB, in string, srid int) tree.Datum {
d, err := tree.ParseDGeometry(in)
require.NoError(t, err)

withSRID, err := d.CloneWithSRID(geopb.SRID(srid))
require.NoError(t, err)

return tree.NewDGeometry(withSRID)
}

type multiArgsBuiltinFixture struct {
Title string
Builtin tree.Overload
Args tree.Datums
Expected tree.Datum
ExpectError bool
}

func TestGeoBuiltinsSTMakeEnvelope(t *testing.T) {
defer leaktest.AfterTest(t)()

// polygonGeometry, err := tree.ParseDGeometry("POLYGON((170 50,170 72,-130 72,-130 50,170 50))")
for _, toPin := range []multiArgsBuiltinFixture{
{
Title: "st_makeenvelope happy path, srid",
Builtin: geoBuiltins["st_makeenvelope"].overloads[0],
Args: tree.Datums{
mustParseDFloat(t, "30.01"),
mustParseDFloat(t, "50.01"),
mustParseDFloat(t, "72.01"),
mustParseDFloat(t, "52.01"),
tree.NewDInt(tree.DInt(4326)),
},
Expected: mustParseDGeometry(t, "POLYGON((30.01 50.01,30.01 52.01,72.01 52.01,72.01 50.01,30.01 50.01))", 4326),
},
{
Title: "st_makeenvelope happy path, unknown srid",
Builtin: geoBuiltins["st_makeenvelope"].overloads[1],
Args: tree.Datums{
mustParseDFloat(t, "30.01"),
mustParseDFloat(t, "50.01"),
mustParseDFloat(t, "72.01"),
mustParseDFloat(t, "52.01"),
},
Expected: mustParseDGeometry(t, "POLYGON((30.01 50.01,30.01 52.01,72.01 52.01,72.01 50.01,30.01 50.01))", 0),
},
{
Title: "st_makeenvelope degenerate bounds, unknown srid",
Builtin: geoBuiltins["st_makeenvelope"].overloads[1],
Args: tree.Datums{
mustParseDFloat(t, "30"),
mustParseDFloat(t, "50"),
mustParseDFloat(t, "30"),
mustParseDFloat(t, "50"),
},
Expected: mustParseDGeometry(t, "POLYGON((30 50,30 50,30 50,30 50,30 50))", 0),
},
} {
testcase := toPin

t.Run(testcase.Title, func(t *testing.T) {
t.Parallel() // SAFE FOR TESTING

overload := testcase.Builtin
datums := testcase.Args
res, err := overload.Fn(&tree.EvalContext{}, datums)
if testcase.ExpectError {
require.Error(t, err)

return
}

require.NoError(t, err)
require.EqualValues(t, testcase.Expected, res)
})
}
}

0 comments on commit 35f2510

Please sign in to comment.