This is an automated email from the ASF dual-hosted git repository.
dongjoon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 9b68d3c92b4c [SPARK-54244][GEO][SQL] Introduce type coercion support
for GEOMETRY data types
9b68d3c92b4c is described below
commit 9b68d3c92b4c7bff7abcb8bb5fe540f2bfefd705
Author: Uros Bojanic <[email protected]>
AuthorDate: Sat Nov 8 07:30:25 2025 -0800
[SPARK-54244][GEO][SQL] Introduce type coercion support for GEOMETRY data
types
### What changes were proposed in this pull request?
Implement least common type (LCT) logic for `GEOMETRY` types, as follows:
- LCT for GeometryType(`srid_1`) and GeometryType(`srid_1`) is:
GeometryType(`srid_1`)
- LCT for GeometryType(`srid_1`) and GeometryType(`srid_2`) is:
GeometryType(`ANY`)
- LCT for GeometryType(`srid_1`) and GeometryType(`ANY`) is:
GeometryType(`ANY`)
- LCT for GeometryType(`ANY`) and GeometryType(`ANY`) is:
GeometryType(`ANY`)
In other words, the mixed SRID `GEOMETRY` type is the *common type* for all
GEOMETRY types.
### Why are the changes needed?
Introducing LCT and type coercion logic in the geospatial data type system.
### Does this PR introduce _any_ user-facing change?
Yes, type coercion is now supported for `GeometryType`.
### How was this patch tested?
Added tests for geometry type coercion:
- `AnsiTypeCoercionSuite`
- `TypeCoercionSuite`
Added appropriate Scala suite unit tests:
- `STExpressionsSuite`
Added appropriate end-to-end SQL tests:
- `st-functions`
### Was this patch authored or co-authored using generative AI tooling?
No.
Closes #52945 from uros-db/geo-coercion-geom.
Authored-by: Uros Bojanic <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
---
.../sql/catalyst/analysis/AnsiTypeCoercion.scala | 3 +
.../spark/sql/catalyst/analysis/TypeCoercion.scala | 3 +
.../catalyst/analysis/AnsiTypeCoercionSuite.scala | 12 ++
.../sql/catalyst/analysis/TypeCoercionSuite.scala | 12 ++
.../analyzer-results/nonansi/st-functions.sql.out | 72 ++++++++++
.../analyzer-results/st-functions.sql.out | 72 ++++++++++
.../resources/sql-tests/inputs/st-functions.sql | 9 ++
.../sql-tests/results/nonansi/st-functions.sql.out | 81 ++++++++++++
.../sql-tests/results/st-functions.sql.out | 81 ++++++++++++
.../org/apache/spark/sql/STExpressionsSuite.scala | 146 +++++++++++++++++++++
10 files changed, 491 insertions(+)
diff --git
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala
index ea4d04ff6e77..e23e7561f0e3 100644
---
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala
+++
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercion.scala
@@ -133,6 +133,9 @@ object AnsiTypeCoercion extends TypeCoercionBase {
// We allow coercion from GEOGRAPHY(<srid>) types (i.e. fixed SRID types)
to the
// GEOGRAPHY(ANY) type (i.e. mixed SRID type). This coercion is always
safe to do.
case (t1: GeographyType, t2: GeographyType) if t1 != t2 =>
Some(GeographyType("ANY"))
+ // We allow coercion from GEOMETRY(<srid>) types (i.e. fixed SRID types)
to the
+ // GEOMETRY(ANY) type (i.e. mixed SRID type). This coercion is always safe
to do.
+ case (t1: GeometryType, t2: GeometryType) if t1 != t2 =>
Some(GeometryType("ANY"))
case (t1, t2) => findTypeForComplex(t1, t2, findTightestCommonType)
}
diff --git
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala
index 9030de9473de..ce387ef397ac 100644
---
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala
+++
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercion.scala
@@ -101,6 +101,9 @@ object TypeCoercion extends TypeCoercionBase {
// We allow coercion from GEOGRAPHY(<srid>) types (i.e. fixed SRID
types) to the
// GEOGRAPHY(ANY) type (i.e. mixed SRID type). This coercion is always
safe to do.
case (t1: GeographyType, t2: GeographyType) if t1 != t2 =>
Some(GeographyType("ANY"))
+ // We allow coercion from GEOMETRY(<srid>) types (i.e. fixed SRID types)
to the
+ // GEOMETRY(ANY) type (i.e. mixed SRID type). This coercion is always
safe to do.
+ case (t1: GeometryType, t2: GeometryType) if t1 != t2 =>
Some(GeometryType("ANY"))
case (t1, t2) => findTypeForComplex(t1, t2, findTightestCommonType)
}
diff --git
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercionSuite.scala
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercionSuite.scala
index 0e5ebcfa313c..fa5027ce259d 100644
---
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercionSuite.scala
+++
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/AnsiTypeCoercionSuite.scala
@@ -180,6 +180,18 @@ class AnsiTypeCoercionSuite extends TypeCoercionSuiteBase {
widenTest(GeographyType("ANY"), GeographyType("ANY"),
Some(GeographyType("ANY")))
widenTest(GeographyType("ANY"), GeographyType(4326),
Some(GeographyType("ANY")))
widenTest(GeographyType(4326), GeographyType("ANY"),
Some(GeographyType("ANY")))
+ // Geometry with same fixed SRIDs.
+ widenTest(GeometryType(0), GeometryType(0), Some(GeometryType(0)))
+ widenTest(GeometryType(3857), GeometryType(3857), Some(GeometryType(3857)))
+ widenTest(GeometryType(4326), GeometryType(4326), Some(GeometryType(4326)))
+ // Geometry with different fixed SRIDs.
+ widenTest(GeometryType(0), GeometryType(3857), Some(GeometryType("ANY")))
+ widenTest(GeometryType(3857), GeometryType(4326),
Some(GeometryType("ANY")))
+ widenTest(GeometryType(4326), GeometryType(0), Some(GeometryType("ANY")))
+ // Geometry with mixed SRIDs.
+ widenTest(GeometryType("ANY"), GeometryType("ANY"),
Some(GeometryType("ANY")))
+ widenTest(GeometryType("ANY"), GeometryType(4326),
Some(GeometryType("ANY")))
+ widenTest(GeometryType(4326), GeometryType("ANY"),
Some(GeometryType("ANY")))
// Integral mixed with floating point.
widenTest(IntegerType, FloatType, Some(DoubleType))
diff --git
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercionSuite.scala
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercionSuite.scala
index 0169034c3475..e6a9690ad757 100644
---
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercionSuite.scala
+++
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/TypeCoercionSuite.scala
@@ -603,6 +603,18 @@ class TypeCoercionSuite extends TypeCoercionSuiteBase {
widenTest(GeographyType("ANY"), GeographyType("ANY"),
Some(GeographyType("ANY")))
widenTest(GeographyType("ANY"), GeographyType(4326),
Some(GeographyType("ANY")))
widenTest(GeographyType(4326), GeographyType("ANY"),
Some(GeographyType("ANY")))
+ // Geometry with same fixed SRIDs.
+ widenTest(GeometryType(0), GeometryType(0), Some(GeometryType(0)))
+ widenTest(GeometryType(3857), GeometryType(3857), Some(GeometryType(3857)))
+ widenTest(GeometryType(4326), GeometryType(4326), Some(GeometryType(4326)))
+ // Geometry with different fixed SRIDs.
+ widenTest(GeometryType(0), GeometryType(3857), Some(GeometryType("ANY")))
+ widenTest(GeometryType(3857), GeometryType(4326),
Some(GeometryType("ANY")))
+ widenTest(GeometryType(4326), GeometryType(0), Some(GeometryType("ANY")))
+ // Geometry with mixed SRIDs.
+ widenTest(GeometryType("ANY"), GeometryType("ANY"),
Some(GeometryType("ANY")))
+ widenTest(GeometryType("ANY"), GeometryType(4326),
Some(GeometryType("ANY")))
+ widenTest(GeometryType(4326), GeometryType("ANY"),
Some(GeometryType("ANY")))
// Integral mixed with floating point.
widenTest(IntegerType, FloatType, Some(FloatType))
diff --git
a/sql/core/src/test/resources/sql-tests/analyzer-results/nonansi/st-functions.sql.out
b/sql/core/src/test/resources/sql-tests/analyzer-results/nonansi/st-functions.sql.out
index 71d5a808e686..c86d2454d759 100644
---
a/sql/core/src/test/resources/sql-tests/analyzer-results/nonansi/st-functions.sql.out
+++
b/sql/core/src/test/resources/sql-tests/analyzer-results/nonansi/st-functions.sql.out
@@ -161,6 +161,14 @@ Project [typeof(array(cast(st_geogfromwkb(wkb#x) as
geography(any)), cast(st_geo
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(array(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query analysis
+Project [typeof(array(cast(st_geomfromwkb(wkb#x) as geometry(any)),
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS
typeof(array(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(map('a', ST_GeogFromWKB(wkb), 'b',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query analysis
@@ -169,6 +177,14 @@ Project [typeof(map(a, cast(st_geogfromwkb(wkb#x) as
geography(any)), b, cast(st
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(map('a', ST_GeomFromWKB(wkb), 'b',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query analysis
+Project [typeof(map(a, cast(st_geomfromwkb(wkb#x) as geometry(any)), b,
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS typeof(map(a,
st_geomfromwkb(wkb), b, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(array(named_struct('g1', ST_GeogFromWKB(wkb), 'g2',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), named_struct('g1',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY), 'g2', ST_GeogFromWKB(wkb)))) FROM geodata
-- !query analysis
@@ -177,6 +193,14 @@ Project [typeof(array(cast(named_struct(g1,
st_geogfromwkb(wkb#x), g2, cast(st_g
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(array(named_struct('g1', ST_GeomFromWKB(wkb), 'g2',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), named_struct('g1',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY), 'g2', ST_GeomFromWKB(wkb)))) FROM geodata
+-- !query analysis
+Project [typeof(array(cast(named_struct(g1, st_geomfromwkb(wkb#x), g2,
cast(st_geomfromwkb(wkb#x) as geometry(any))) as
struct<g1:geometry(any),g2:geometry(any)>), cast(named_struct(g1,
cast(st_geomfromwkb(wkb#x) as geometry(any)), g2, st_geomfromwkb(wkb#x)) as
struct<g1:geometry(any),g2:geometry(any)>))) AS typeof(array(named_struct(g1,
st_geomfromwkb(wkb), g2, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))),
named_struct(g1, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), g2,
st_geomfromwkb(wk [...]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(named_struct('a', array(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), 'b', map('g', ST_GeogFromWKB(wkb), 'h',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))) FROM geodata
-- !query analysis
@@ -185,6 +209,14 @@ Project [typeof(named_struct(a,
array(cast(st_geogfromwkb(wkb#x) as geography(an
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(named_struct('a', array(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), 'b', map('g', ST_GeomFromWKB(wkb), 'h',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))) FROM geodata
+-- !query analysis
+Project [typeof(named_struct(a, array(cast(st_geomfromwkb(wkb#x) as
geometry(any)), cast(st_geomfromwkb(wkb#x) as geometry(any))), b, map(g,
cast(st_geomfromwkb(wkb#x) as geometry(any)), h, cast(st_geomfromwkb(wkb#x) as
geometry(any))))) AS typeof(named_struct(a, array(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), b, map(g, st_geomfromwkb(wkb), h,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(nvl(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata
-- !query analysis
@@ -193,6 +225,14 @@ Project [typeof(nvl(st_geogfromwkb(wkb#x),
cast(st_geogfromwkb(wkb#x) as geograp
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(nvl(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query analysis
+Project [typeof(nvl(st_geomfromwkb(wkb#x), cast(st_geomfromwkb(wkb#x) as
geometry(any)))) AS typeof(nvl(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(nvl2(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query analysis
@@ -201,6 +241,14 @@ Project [typeof(nvl2(st_geogfromwkb(wkb#x),
cast(st_geogfromwkb(wkb#x) as geogra
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(nvl2(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query analysis
+Project [typeof(nvl2(st_geomfromwkb(wkb#x), cast(st_geomfromwkb(wkb#x) as
geometry(any)), st_geomfromwkb(wkb#x))) AS typeof(nvl2(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), st_geomfromwkb(wkb)))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY) ELSE ST_GeogFromWKB(wkb) END) FROM geodata
-- !query analysis
@@ -209,6 +257,14 @@ Project [typeof(CASE WHEN isnotnull(wkb#x) THEN
cast(st_geogfromwkb(wkb#x) as ge
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeomFromWKB(wkb)::GEOMETRY(ANY) ELSE ST_GeomFromWKB(wkb) END) FROM geodata
+-- !query analysis
+Project [typeof(CASE WHEN isnotnull(wkb#x) THEN cast(st_geomfromwkb(wkb#x) as
geometry(any)) ELSE cast(st_geomfromwkb(wkb#x) as geometry(any)) END) AS
typeof(CASE WHEN (wkb IS NOT NULL) THEN CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)) ELSE st_geomfromwkb(wkb) END)#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(coalesce(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query analysis
@@ -217,6 +273,14 @@ Project [typeof(coalesce(cast(st_geogfromwkb(wkb#x) as
geography(any)), cast(st_
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(coalesce(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query analysis
+Project [typeof(coalesce(cast(st_geomfromwkb(wkb#x) as geometry(any)),
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS
typeof(coalesce(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(IF(wkb IS NOT NULL, ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query analysis
@@ -225,6 +289,14 @@ Project [typeof(if (isnotnull(wkb#x))
cast(st_geogfromwkb(wkb#x) as geography(an
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(IF(wkb IS NOT NULL, ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query analysis
+Project [typeof(if (isnotnull(wkb#x)) cast(st_geomfromwkb(wkb#x) as
geometry(any)) else cast(st_geomfromwkb(wkb#x) as geometry(any))) AS
typeof((IF((wkb IS NOT NULL), CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)),
st_geomfromwkb(wkb))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT
hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000f03f0000000000000040')))
AS result
-- !query analysis
diff --git
a/sql/core/src/test/resources/sql-tests/analyzer-results/st-functions.sql.out
b/sql/core/src/test/resources/sql-tests/analyzer-results/st-functions.sql.out
index 71d5a808e686..c86d2454d759 100644
---
a/sql/core/src/test/resources/sql-tests/analyzer-results/st-functions.sql.out
+++
b/sql/core/src/test/resources/sql-tests/analyzer-results/st-functions.sql.out
@@ -161,6 +161,14 @@ Project [typeof(array(cast(st_geogfromwkb(wkb#x) as
geography(any)), cast(st_geo
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(array(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query analysis
+Project [typeof(array(cast(st_geomfromwkb(wkb#x) as geometry(any)),
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS
typeof(array(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(map('a', ST_GeogFromWKB(wkb), 'b',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query analysis
@@ -169,6 +177,14 @@ Project [typeof(map(a, cast(st_geogfromwkb(wkb#x) as
geography(any)), b, cast(st
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(map('a', ST_GeomFromWKB(wkb), 'b',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query analysis
+Project [typeof(map(a, cast(st_geomfromwkb(wkb#x) as geometry(any)), b,
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS typeof(map(a,
st_geomfromwkb(wkb), b, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(array(named_struct('g1', ST_GeogFromWKB(wkb), 'g2',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), named_struct('g1',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY), 'g2', ST_GeogFromWKB(wkb)))) FROM geodata
-- !query analysis
@@ -177,6 +193,14 @@ Project [typeof(array(cast(named_struct(g1,
st_geogfromwkb(wkb#x), g2, cast(st_g
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(array(named_struct('g1', ST_GeomFromWKB(wkb), 'g2',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), named_struct('g1',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY), 'g2', ST_GeomFromWKB(wkb)))) FROM geodata
+-- !query analysis
+Project [typeof(array(cast(named_struct(g1, st_geomfromwkb(wkb#x), g2,
cast(st_geomfromwkb(wkb#x) as geometry(any))) as
struct<g1:geometry(any),g2:geometry(any)>), cast(named_struct(g1,
cast(st_geomfromwkb(wkb#x) as geometry(any)), g2, st_geomfromwkb(wkb#x)) as
struct<g1:geometry(any),g2:geometry(any)>))) AS typeof(array(named_struct(g1,
st_geomfromwkb(wkb), g2, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))),
named_struct(g1, CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), g2,
st_geomfromwkb(wk [...]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(named_struct('a', array(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), 'b', map('g', ST_GeogFromWKB(wkb), 'h',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))) FROM geodata
-- !query analysis
@@ -185,6 +209,14 @@ Project [typeof(named_struct(a,
array(cast(st_geogfromwkb(wkb#x) as geography(an
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(named_struct('a', array(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), 'b', map('g', ST_GeomFromWKB(wkb), 'h',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))) FROM geodata
+-- !query analysis
+Project [typeof(named_struct(a, array(cast(st_geomfromwkb(wkb#x) as
geometry(any)), cast(st_geomfromwkb(wkb#x) as geometry(any))), b, map(g,
cast(st_geomfromwkb(wkb#x) as geometry(any)), h, cast(st_geomfromwkb(wkb#x) as
geometry(any))))) AS typeof(named_struct(a, array(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), b, map(g, st_geomfromwkb(wkb), h,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(nvl(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata
-- !query analysis
@@ -193,6 +225,14 @@ Project [typeof(nvl(st_geogfromwkb(wkb#x),
cast(st_geogfromwkb(wkb#x) as geograp
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(nvl(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query analysis
+Project [typeof(nvl(st_geomfromwkb(wkb#x), cast(st_geomfromwkb(wkb#x) as
geometry(any)))) AS typeof(nvl(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(nvl2(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query analysis
@@ -201,6 +241,14 @@ Project [typeof(nvl2(st_geogfromwkb(wkb#x),
cast(st_geogfromwkb(wkb#x) as geogra
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(nvl2(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query analysis
+Project [typeof(nvl2(st_geomfromwkb(wkb#x), cast(st_geomfromwkb(wkb#x) as
geometry(any)), st_geomfromwkb(wkb#x))) AS typeof(nvl2(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), st_geomfromwkb(wkb)))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY) ELSE ST_GeogFromWKB(wkb) END) FROM geodata
-- !query analysis
@@ -209,6 +257,14 @@ Project [typeof(CASE WHEN isnotnull(wkb#x) THEN
cast(st_geogfromwkb(wkb#x) as ge
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeomFromWKB(wkb)::GEOMETRY(ANY) ELSE ST_GeomFromWKB(wkb) END) FROM geodata
+-- !query analysis
+Project [typeof(CASE WHEN isnotnull(wkb#x) THEN cast(st_geomfromwkb(wkb#x) as
geometry(any)) ELSE cast(st_geomfromwkb(wkb#x) as geometry(any)) END) AS
typeof(CASE WHEN (wkb IS NOT NULL) THEN CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)) ELSE st_geomfromwkb(wkb) END)#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(coalesce(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query analysis
@@ -217,6 +273,14 @@ Project [typeof(coalesce(cast(st_geogfromwkb(wkb#x) as
geography(any)), cast(st_
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(coalesce(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query analysis
+Project [typeof(coalesce(cast(st_geomfromwkb(wkb#x) as geometry(any)),
cast(st_geomfromwkb(wkb#x) as geometry(any)))) AS
typeof(coalesce(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT typeof(IF(wkb IS NOT NULL, ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query analysis
@@ -225,6 +289,14 @@ Project [typeof(if (isnotnull(wkb#x))
cast(st_geogfromwkb(wkb#x) as geography(an
+- Relation spark_catalog.default.geodata[wkb#x] parquet
+-- !query
+SELECT typeof(IF(wkb IS NOT NULL, ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query analysis
+Project [typeof(if (isnotnull(wkb#x)) cast(st_geomfromwkb(wkb#x) as
geometry(any)) else cast(st_geomfromwkb(wkb#x) as geometry(any))) AS
typeof((IF((wkb IS NOT NULL), CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)),
st_geomfromwkb(wkb))))#x]
++- SubqueryAlias spark_catalog.default.geodata
+ +- Relation spark_catalog.default.geodata[wkb#x] parquet
+
+
-- !query
SELECT
hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000f03f0000000000000040')))
AS result
-- !query analysis
diff --git a/sql/core/src/test/resources/sql-tests/inputs/st-functions.sql
b/sql/core/src/test/resources/sql-tests/inputs/st-functions.sql
index e1c11bb089b5..70fbdef53330 100644
--- a/sql/core/src/test/resources/sql-tests/inputs/st-functions.sql
+++ b/sql/core/src/test/resources/sql-tests/inputs/st-functions.sql
@@ -32,23 +32,32 @@ SELECT
CAST(ST_GeomFromWKB(X'0101000000000000000000f03f0000000000000040')::GEOME
-- Array
SELECT typeof(array(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata;
+SELECT typeof(array(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata;
-- Map
SELECT typeof(map('a', ST_GeogFromWKB(wkb), 'b',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata;
+SELECT typeof(map('a', ST_GeomFromWKB(wkb), 'b',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata;
-- Struct
SELECT typeof(array(named_struct('g1', ST_GeogFromWKB(wkb), 'g2',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), named_struct('g1',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY), 'g2', ST_GeogFromWKB(wkb)))) FROM geodata;
+SELECT typeof(array(named_struct('g1', ST_GeomFromWKB(wkb), 'g2',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), named_struct('g1',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY), 'g2', ST_GeomFromWKB(wkb)))) FROM geodata;
-- Nested
SELECT typeof(named_struct('a', array(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), 'b', map('g', ST_GeogFromWKB(wkb), 'h',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))) FROM geodata;
+SELECT typeof(named_struct('a', array(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), 'b', map('g', ST_GeomFromWKB(wkb), 'h',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))) FROM geodata;
-- NVL
SELECT typeof(nvl(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata;
+SELECT typeof(nvl(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata;
-- NVL2
SELECT typeof(nvl2(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata;
+SELECT typeof(nvl2(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata;
-- CASE WHEN
SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY) ELSE ST_GeogFromWKB(wkb) END) FROM geodata;
+SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeomFromWKB(wkb)::GEOMETRY(ANY) ELSE ST_GeomFromWKB(wkb) END) FROM geodata;
-- COALESCE
SELECT typeof(coalesce(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata;
+SELECT typeof(coalesce(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata;
-- IF
SELECT typeof(IF(wkb IS NOT NULL, ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata;
+SELECT typeof(IF(wkb IS NOT NULL, ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata;
---- ST reader/writer expressions
diff --git
a/sql/core/src/test/resources/sql-tests/results/nonansi/st-functions.sql.out
b/sql/core/src/test/resources/sql-tests/results/nonansi/st-functions.sql.out
index 71a7d6eaa5dc..95e7412bfea4 100644
--- a/sql/core/src/test/resources/sql-tests/results/nonansi/st-functions.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/nonansi/st-functions.sql.out
@@ -178,6 +178,15 @@ array<geography(any)>
array<geography(any)>
+-- !query
+SELECT typeof(array(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query schema
+struct<typeof(array(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+array<geometry(any)>
+array<geometry(any)>
+
+
-- !query
SELECT typeof(map('a', ST_GeogFromWKB(wkb), 'b',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query schema
@@ -187,6 +196,15 @@ map<string,geography(any)>
map<string,geography(any)>
+-- !query
+SELECT typeof(map('a', ST_GeomFromWKB(wkb), 'b',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query schema
+struct<typeof(map(a, st_geomfromwkb(wkb), b, CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+map<string,geometry(any)>
+map<string,geometry(any)>
+
+
-- !query
SELECT typeof(array(named_struct('g1', ST_GeogFromWKB(wkb), 'g2',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), named_struct('g1',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY), 'g2', ST_GeogFromWKB(wkb)))) FROM geodata
-- !query schema
@@ -196,6 +214,15 @@ array<struct<g1:geography(any),g2:geography(any)>>
array<struct<g1:geography(any),g2:geography(any)>>
+-- !query
+SELECT typeof(array(named_struct('g1', ST_GeomFromWKB(wkb), 'g2',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), named_struct('g1',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY), 'g2', ST_GeomFromWKB(wkb)))) FROM geodata
+-- !query schema
+struct<typeof(array(named_struct(g1, st_geomfromwkb(wkb), g2,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), named_struct(g1,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), g2, st_geomfromwkb(wkb)))):string>
+-- !query output
+array<struct<g1:geometry(any),g2:geometry(any)>>
+array<struct<g1:geometry(any),g2:geometry(any)>>
+
+
-- !query
SELECT typeof(named_struct('a', array(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), 'b', map('g', ST_GeogFromWKB(wkb), 'h',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))) FROM geodata
-- !query schema
@@ -205,6 +232,15 @@
struct<a:array<geography(any)>,b:map<string,geography(any)>>
struct<a:array<geography(any)>,b:map<string,geography(any)>>
+-- !query
+SELECT typeof(named_struct('a', array(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), 'b', map('g', ST_GeomFromWKB(wkb), 'h',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))) FROM geodata
+-- !query schema
+struct<typeof(named_struct(a, array(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), b, map(g, st_geomfromwkb(wkb), h,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))))):string>
+-- !query output
+struct<a:array<geometry(any)>,b:map<string,geometry(any)>>
+struct<a:array<geometry(any)>,b:map<string,geometry(any)>>
+
+
-- !query
SELECT typeof(nvl(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata
-- !query schema
@@ -214,6 +250,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(nvl(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query schema
+struct<typeof(nvl(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(nvl2(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query schema
@@ -223,6 +268,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(nvl2(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query schema
+struct<typeof(nvl2(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)), st_geomfromwkb(wkb))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY) ELSE ST_GeogFromWKB(wkb) END) FROM geodata
-- !query schema
@@ -232,6 +286,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeomFromWKB(wkb)::GEOMETRY(ANY) ELSE ST_GeomFromWKB(wkb) END) FROM geodata
+-- !query schema
+struct<typeof(CASE WHEN (wkb IS NOT NULL) THEN CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)) ELSE st_geomfromwkb(wkb) END):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(coalesce(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query schema
@@ -241,6 +304,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(coalesce(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query schema
+struct<typeof(coalesce(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(IF(wkb IS NOT NULL, ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query schema
@@ -250,6 +322,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(IF(wkb IS NOT NULL, ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query schema
+struct<typeof((IF((wkb IS NOT NULL), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)), st_geomfromwkb(wkb)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT
hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000f03f0000000000000040')))
AS result
-- !query schema
diff --git a/sql/core/src/test/resources/sql-tests/results/st-functions.sql.out
b/sql/core/src/test/resources/sql-tests/results/st-functions.sql.out
index 71a7d6eaa5dc..95e7412bfea4 100644
--- a/sql/core/src/test/resources/sql-tests/results/st-functions.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/st-functions.sql.out
@@ -178,6 +178,15 @@ array<geography(any)>
array<geography(any)>
+-- !query
+SELECT typeof(array(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query schema
+struct<typeof(array(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+array<geometry(any)>
+array<geometry(any)>
+
+
-- !query
SELECT typeof(map('a', ST_GeogFromWKB(wkb), 'b',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query schema
@@ -187,6 +196,15 @@ map<string,geography(any)>
map<string,geography(any)>
+-- !query
+SELECT typeof(map('a', ST_GeomFromWKB(wkb), 'b',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query schema
+struct<typeof(map(a, st_geomfromwkb(wkb), b, CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+map<string,geometry(any)>
+map<string,geometry(any)>
+
+
-- !query
SELECT typeof(array(named_struct('g1', ST_GeogFromWKB(wkb), 'g2',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), named_struct('g1',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY), 'g2', ST_GeogFromWKB(wkb)))) FROM geodata
-- !query schema
@@ -196,6 +214,15 @@ array<struct<g1:geography(any),g2:geography(any)>>
array<struct<g1:geography(any),g2:geography(any)>>
+-- !query
+SELECT typeof(array(named_struct('g1', ST_GeomFromWKB(wkb), 'g2',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), named_struct('g1',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY), 'g2', ST_GeomFromWKB(wkb)))) FROM geodata
+-- !query schema
+struct<typeof(array(named_struct(g1, st_geomfromwkb(wkb), g2,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), named_struct(g1,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY)), g2, st_geomfromwkb(wkb)))):string>
+-- !query output
+array<struct<g1:geometry(any),g2:geometry(any)>>
+array<struct<g1:geometry(any),g2:geometry(any)>>
+
+
-- !query
SELECT typeof(named_struct('a', array(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)), 'b', map('g', ST_GeogFromWKB(wkb), 'h',
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))) FROM geodata
-- !query schema
@@ -205,6 +232,15 @@
struct<a:array<geography(any)>,b:map<string,geography(any)>>
struct<a:array<geography(any)>,b:map<string,geography(any)>>
+-- !query
+SELECT typeof(named_struct('a', array(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)), 'b', map('g', ST_GeomFromWKB(wkb), 'h',
ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))) FROM geodata
+-- !query schema
+struct<typeof(named_struct(a, array(st_geomfromwkb(wkb),
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))), b, map(g, st_geomfromwkb(wkb), h,
CAST(st_geomfromwkb(wkb) AS GEOMETRY(ANY))))):string>
+-- !query output
+struct<a:array<geometry(any)>,b:map<string,geometry(any)>>
+struct<a:array<geometry(any)>,b:map<string,geometry(any)>>
+
+
-- !query
SELECT typeof(nvl(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY)))
FROM geodata
-- !query schema
@@ -214,6 +250,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(nvl(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY)))
FROM geodata
+-- !query schema
+struct<typeof(nvl(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(nvl2(ST_GeogFromWKB(wkb), ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query schema
@@ -223,6 +268,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(nvl2(ST_GeomFromWKB(wkb), ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query schema
+struct<typeof(nvl2(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)), st_geomfromwkb(wkb))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY) ELSE ST_GeogFromWKB(wkb) END) FROM geodata
-- !query schema
@@ -232,6 +286,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(CASE WHEN wkb IS NOT NULL THEN
ST_GeomFromWKB(wkb)::GEOMETRY(ANY) ELSE ST_GeomFromWKB(wkb) END) FROM geodata
+-- !query schema
+struct<typeof(CASE WHEN (wkb IS NOT NULL) THEN CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)) ELSE st_geomfromwkb(wkb) END):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(coalesce(ST_GeogFromWKB(wkb),
ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY))) FROM geodata
-- !query schema
@@ -241,6 +304,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(coalesce(ST_GeomFromWKB(wkb),
ST_GeomFromWKB(wkb)::GEOMETRY(ANY))) FROM geodata
+-- !query schema
+struct<typeof(coalesce(st_geomfromwkb(wkb), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT typeof(IF(wkb IS NOT NULL, ST_GeogFromWKB(wkb)::GEOGRAPHY(ANY),
ST_GeogFromWKB(wkb))) FROM geodata
-- !query schema
@@ -250,6 +322,15 @@ geography(any)
geography(any)
+-- !query
+SELECT typeof(IF(wkb IS NOT NULL, ST_GeomFromWKB(wkb)::GEOMETRY(ANY),
ST_GeomFromWKB(wkb))) FROM geodata
+-- !query schema
+struct<typeof((IF((wkb IS NOT NULL), CAST(st_geomfromwkb(wkb) AS
GEOMETRY(ANY)), st_geomfromwkb(wkb)))):string>
+-- !query output
+geometry(any)
+geometry(any)
+
+
-- !query
SELECT
hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000f03f0000000000000040')))
AS result
-- !query schema
diff --git
a/sql/core/src/test/scala/org/apache/spark/sql/STExpressionsSuite.scala
b/sql/core/src/test/scala/org/apache/spark/sql/STExpressionsSuite.scala
index 6dd8f4347105..4f0d567fccc4 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/STExpressionsSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/STExpressionsSuite.scala
@@ -271,6 +271,152 @@ class STExpressionsSuite
}
}
+ test("CreateArray with GEOMETRY literals") {
+ // Test data: WKB representation of POINT(1 2).
+ val wkbString = "0101000000000000000000F03F0000000000000040"
+ // Test with literals, using geometries with different SRID values.
+ val geom1 = s"ST_GeomFromWKB(X'$wkbString')" // Literal with fixed SRID
(0).
+ val geometryType1 = GeometryType(0)
+ val geom2 = s"$geom1::GEOMETRY(ANY)" // Literal with mixed SRID (ANY).
+ val geometryType2 = GeometryType("ANY")
+ val geo = "hex(ST_AsBinary(g)), ST_Srid(g)"
+ val row = Row(wkbString, 0)
+
+ val testCases = Seq(
+ (s"array($geom1)", geometryType1, Seq(row)),
+ (s"array($geom2)", geometryType2, Seq(row)),
+ (s"array($geom1, $geom1)", geometryType1, Seq(row, row)),
+ (s"array($geom2, $geom2)", geometryType2, Seq(row, row)),
+ (s"array($geom1, $geom2)", mixedSridGeometryType, Seq(row, row)),
+ (s"array($geom2, $geom1)", mixedSridGeometryType, Seq(row, row))
+ )
+
+ for ((expr, expectedType, expectedRows) <- testCases) {
+ assertType(
+ s"SELECT $expr",
+ ArrayType(expectedType)
+ )
+ checkAnswer(
+ sql(s"WITH t AS (SELECT explode($expr) AS g) SELECT $geo FROM t"),
+ expectedRows
+ )
+ }
+ }
+
+ test("CreateArray with GEOMETRY columns") {
+ // Test data: WKB representation of POINT(1 2).
+ val wkbString = "0101000000000000000000F03F0000000000000040"
+ // Test with columns, using geometries with different SRID values.
+ val geom1 = "ST_GeomFromWKB(wkb)" // Column with fixed SRID (0).
+ val geometryType1 = GeometryType(0)
+ val geom2 = s"$geom1::GEOMETRY(ANY)" // Column with mixed SRID (ANY).
+ val geometryType2 = GeometryType("ANY")
+ val geo = "hex(ST_AsBinary(g)), ST_Srid(g)"
+ val row = Row(wkbString, 0)
+
+ val testCases = Seq(
+ (s"array($geom1)", geometryType1, Seq(row)),
+ (s"array($geom2)", geometryType2, Seq(row)),
+ (s"array($geom1, $geom1)", geometryType1, Seq(row, row)),
+ (s"array($geom2, $geom2)", geometryType2, Seq(row, row)),
+ (s"array($geom1, $geom2)", mixedSridGeometryType, Seq(row, row)),
+ (s"array($geom2, $geom1)", mixedSridGeometryType, Seq(row, row))
+ )
+
+ // Test with literal and column, using geometries with different SRID
values.
+ withTable("tbl") {
+ // Construct and populate the test table.
+ sql("CREATE TABLE tbl (wkb BINARY)")
+ sql(s"INSERT INTO tbl VALUES (X'$wkbString')")
+
+ for ((query, expectedType, expectedRows) <- testCases) {
+ assertType(
+ s"SELECT $query FROM tbl",
+ ArrayType(expectedType)
+ )
+ checkAnswer(
+ sql(s"WITH t AS (SELECT explode($query) AS g FROM tbl) SELECT $geo
FROM t"),
+ expectedRows
+ )
+ }
+ }
+ }
+
+ test("NVL with GEOMETRY literals") {
+ // Test data: WKB representation of POINT(1 2).
+ val wkbString = "0101000000000000000000F03F0000000000000040"
+ // Test with literals, using geometries with different SRID values.
+ val geom1 = s"ST_GeomFromWKB(X'$wkbString')" // Literal with fixed SRID
(0).
+ val geometryType1 = GeometryType(0)
+ val geom2 = s"$geom1::GEOMETRY(ANY)" // Literal with mixed SRID (ANY).
+ val geometryType2 = GeometryType("ANY")
+ val geo = "hex(ST_AsBinary(g)), ST_Srid(g)"
+ val row = Row(wkbString, 0)
+
+ val testCases = Seq(
+ (s"nvl(null, $geom1)", geometryType1, Seq(row)),
+ (s"nvl($geom1, null)", geometryType1, Seq(row)),
+ (s"nvl(null, $geom2)", geometryType2, Seq(row)),
+ (s"nvl($geom2, null)", geometryType2, Seq(row)),
+ (s"nvl($geom1, $geom1)", geometryType1, Seq(row)),
+ (s"nvl($geom2, $geom2)", geometryType2, Seq(row)),
+ (s"nvl($geom1, $geom2)", mixedSridGeometryType, Seq(row)),
+ (s"nvl($geom2, $geom1)", mixedSridGeometryType, Seq(row))
+ )
+
+ for ((expr, expectedType, expectedRows) <- testCases) {
+ assertType(
+ s"SELECT $expr",
+ expectedType
+ )
+ checkAnswer(
+ sql(s"WITH t AS (SELECT $expr AS g) SELECT $geo FROM t"),
+ expectedRows
+ )
+ }
+ }
+
+ test("NVL with GEOMETRY columns") {
+ // Test data: WKB representation of POINT(1 2).
+ val wkbString = "0101000000000000000000F03F0000000000000040"
+ // Test with columns, using geometries with different SRID values.
+ val geom1 = "ST_GeomFromWKB(wkb)" // Column with fixed SRID (0).
+ val geometryType1 = GeometryType(0)
+ val geom2 = s"$geom1::GEOMETRY(ANY)" // Column with mixed SRID (ANY).
+ val geometryType2 = GeometryType("ANY")
+ val geo = "hex(ST_AsBinary(g)), ST_Srid(g)"
+ val row = Row(wkbString, 0)
+
+ val testCases = Seq(
+ (s"nvl(null, $geom1)", geometryType1, Seq(row)),
+ (s"nvl($geom1, null)", geometryType1, Seq(row)),
+ (s"nvl(null, $geom2)", geometryType2, Seq(row)),
+ (s"nvl($geom2, null)", geometryType2, Seq(row)),
+ (s"nvl($geom1, $geom1)", geometryType1, Seq(row)),
+ (s"nvl($geom2, $geom2)", geometryType2, Seq(row)),
+ (s"nvl($geom1, $geom2)", mixedSridGeometryType, Seq(row)),
+ (s"nvl($geom2, $geom1)", mixedSridGeometryType, Seq(row))
+ )
+
+ // Test with literal and column, using geometries with different SRID
values.
+ withTable("tbl") {
+ // Construct and populate the test table.
+ sql("CREATE TABLE tbl (wkb BINARY)")
+ sql(s"INSERT INTO tbl VALUES (X'$wkbString')")
+
+ for ((query, expectedType, expectedRows) <- testCases) {
+ assertType(
+ s"SELECT $query FROM tbl",
+ expectedType
+ )
+ checkAnswer(
+ sql(s"WITH t AS (SELECT $query AS g FROM tbl) SELECT $geo FROM t"),
+ expectedRows
+ )
+ }
+ }
+ }
+
/** ST reader/writer expressions. */
test("ST_AsBinary") {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]