From 95b0bcb565708dcca276c3f084cf6291dd54730a Mon Sep 17 00:00:00 2001 From: "yukkit.zhang" Date: Thu, 31 Aug 2023 11:20:33 +0800 Subject: [PATCH 1/4] feat: support gis function st_distance --- Cargo.lock | 170 ++++++++++- Cargo.toml | 2 + common/models/src/schema.rs | 2 +- query_server/query/Cargo.toml | 2 + .../extension/expr/scalar_function/gis/mod.rs | 84 ++++++ .../expr/scalar_function/gis/st_distance.rs | 266 ++++++++++++++++++ .../src/extension/expr/scalar_function/mod.rs | 2 + .../cases/function/gis/setup.slt | 29 ++ .../cases/function/gis/st_distance.slt | 191 +++++++++++++ 9 files changed, 745 insertions(+), 3 deletions(-) create mode 100644 query_server/query/src/extension/expr/scalar_function/gis/mod.rs create mode 100644 query_server/query/src/extension/expr/scalar_function/gis/st_distance.rs create mode 100644 query_server/sqllogicaltests/cases/function/gis/setup.slt create mode 100644 query_server/sqllogicaltests/cases/function/gis/st_distance.slt diff --git a/Cargo.lock b/Cargo.lock index ba844f2e3..800c163a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,15 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -680,6 +689,15 @@ dependencies = [ "syn 2.0.25", ] +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "atty" version = "0.2.14" @@ -1421,6 +1439,12 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -1934,6 +1958,16 @@ dependencies = [ "walkdir", ] +[[package]] +name = "earcutr" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0812b44697951d35fde8fcb0da81c9de7e809e825a66bbf1ecb79d9829d4ca3d" +dependencies = [ + "itertools 0.10.5", + "num-traits", +] + [[package]] name = "educe" version = "0.4.22" @@ -2135,6 +2169,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "fnv" version = "1.0.7" @@ -2313,6 +2353,70 @@ dependencies = [ "version_check", ] +[[package]] +name = "geo" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1645cf1d7fea7dac1a66f7357f3df2677ada708b8d9db8e9b043878930095a96" +dependencies = [ + "earcutr", + "float_next_after", + "geo-types", + "geographiclib-rs", + "log", + "num-traits", + "robust", + "rstar", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "rstar", + "serde", +] + +[[package]] +name = "geographiclib-rs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea804e7bd3c6a4ca6a01edfa35231557a8a81d4d3f3e1e2b650d028c42592be" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "geozero" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b1b9a1eeae9ad09e12ec50243956105184b26440f81f978cd3aae009b214d4d" +dependencies = [ + "geo-types", + "geojson", + "log", + "scroll", + "serde_json", + "thiserror", + "wkt", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -2371,6 +2475,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2425,6 +2538,19 @@ dependencies = [ "http", ] +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin 0.9.8", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -4286,6 +4412,8 @@ dependencies = [ "dirs", "flatbuffers 22.12.6", "futures", + "geo", + "geozero", "lazy_static", "libc", "memory_pool", @@ -4595,6 +4723,23 @@ dependencies = [ "retain_mut", ] +[[package]] +name = "robust" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" + +[[package]] +name = "rstar" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" +dependencies = [ + "heapless", + "num-traits", + "smallvec", +] + [[package]] name = "run_script" version = "0.10.1" @@ -4771,6 +4916,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" + [[package]] name = "sct" version = "0.7.0" @@ -4818,9 +4969,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -5046,6 +5197,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "sqllogicaltests" @@ -6375,6 +6529,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "wkt" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c2252781f8927974e8ba6a67c965a759a2b88ea2b1825f6862426bbb1c8f41" +dependencies = [ + "geo-types", + "log", + "num-traits", + "thiserror", +] + [[package]] name = "xz2" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index 37a6ccac1..47e0b1a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,6 +152,8 @@ opentelemetry_sdk = { version = "0.19.0" } opentelemetry_api = { version = "0.19.0" } dateparser = "0.1.7" run_script = "0.10.1" +geo = "0.26.0" +geozero = "0.11.0" [workspace.package] edition = "2021" diff --git a/common/models/src/schema.rs b/common/models/src/schema.rs index f1a42781b..ffe09548a 100644 --- a/common/models/src/schema.rs +++ b/common/models/src/schema.rs @@ -536,7 +536,7 @@ impl ColumnType { Self::Field(ValueType::Integer) => 1, Self::Field(ValueType::Unsigned) => 2, Self::Field(ValueType::Boolean) => 3, - Self::Field(ValueType::String) => 4, + Self::Field(ValueType::String) | Self::Field(ValueType::Geometry(_)) => 4, _ => 0, } } diff --git a/query_server/query/Cargo.toml b/query_server/query/Cargo.toml index 3f2cc3c7b..4ff699356 100644 --- a/query_server/query/Cargo.toml +++ b/query_server/query/Cargo.toml @@ -48,6 +48,8 @@ async-backtrace = { workspace = true, optional = true } bincode = { workspace = true } dirs = { workspace = true } once_cell = { workspace = true } +geo = { workspace = true } +geozero = { workspace = true, features = ["with-wkb"]} [features] default = [] diff --git a/query_server/query/src/extension/expr/scalar_function/gis/mod.rs b/query_server/query/src/extension/expr/scalar_function/gis/mod.rs new file mode 100644 index 000000000..55d600148 --- /dev/null +++ b/query_server/query/src/extension/expr/scalar_function/gis/mod.rs @@ -0,0 +1,84 @@ +mod st_distance; + +use spi::query::function::FunctionMetadataManager; +use spi::Result; + +pub fn register_udfs(func_manager: &mut dyn FunctionMetadataManager) -> Result<()> { + st_distance::register_udf(func_manager)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use geo::{line_string, point, EuclideanDistance, Geometry, Point}; + use geozero::wkb::Wkb; + use geozero::wkt::WktStr; + use geozero::{CoordDimensions, ToGeo, ToWkb, ToWkt}; + + #[test] + fn test_from_wkb() { + let p = point!(x: 1.2, y: 1.3); + let pe: Geometry = p.into(); + let bytes = pe.to_wkb(CoordDimensions::xy()).unwrap(); + + let wkb = Wkb(bytes); + + let geo = wkb.to_geo().unwrap(); + + match geo { + Geometry::Point(p) => { + assert_eq!(Point::new(1.2, 1.3), p) + } + _ => panic!("Expected Point"), + } + } + + #[test] + fn test_from_wkt() { + // POINT + // LINESTRING + // POLYGON + // MULTIPOINT + // MULTILINESTRING + // MULTIPOLYGON + // GEOMETRYCOLLECTION + // https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry + let wht = WktStr("POINT(1.2 1.3)"); + + let geo = wht.to_geo().unwrap(); + + match geo { + Geometry::Point(p) => { + assert_eq!(Point::new(1.2, 1.3), p) + } + _ => panic!("Expected Point"), + } + } + + #[test] + fn test_to_wkt() { + let p = point!(x: 1.2, y: 1.3); + let pe: Geometry = p.into(); + + let wkt = pe.to_wkt().unwrap(); + + assert_eq!("POINT(1.2 1.3)", &wkt) + } + + /// Calculate the minimum euclidean distance between geometries + #[test] + fn test_euclidean_distance() { + let p = point!(x: 1.2, y: 1.3); + + let ls = line_string![ + (x: -21.95156, y: 64.1446), + (x: -21.951, y: 64.14479), + (x: -21.95044, y: 64.14527), + (x: -21.951445, y: 64.145508), + ]; + + let dist = ls.euclidean_distance(&p); + + assert_eq!(66.9734009226357, dist) + } +} diff --git a/query_server/query/src/extension/expr/scalar_function/gis/st_distance.rs b/query_server/query/src/extension/expr/scalar_function/gis/st_distance.rs new file mode 100644 index 000000000..f30d7b903 --- /dev/null +++ b/query_server/query/src/extension/expr/scalar_function/gis/st_distance.rs @@ -0,0 +1,266 @@ +use std::sync::Arc; + +use datafusion::arrow::array::{downcast_array, ArrayRef, Float64Builder, StringArray}; +use datafusion::arrow::datatypes::DataType; +use datafusion::common::Result as DFResult; +use datafusion::error::DataFusionError; +use datafusion::logical_expr::{ReturnTypeFunction, ScalarUDF, Signature, Volatility}; +use datafusion::physical_plan::functions::make_scalar_function; +use geo::{ + EuclideanDistance, Geometry, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, + Point, Polygon, Triangle, +}; +use geozero::wkt::WktStr; +use geozero::ToGeo; +use spi::query::function::FunctionMetadataManager; +use spi::Result; + +pub fn register_udf(func_manager: &mut dyn FunctionMetadataManager) -> Result { + let udf = new(); + func_manager.register_udf(udf.clone())?; + Ok(udf) +} + +fn new() -> ScalarUDF { + let fun = make_scalar_function(func); + + let signature = Signature::exact(vec![DataType::Utf8, DataType::Utf8], Volatility::Immutable); + let return_type: ReturnTypeFunction = Arc::new(move |_| Ok(Arc::new(DataType::Float64))); + + ScalarUDF::new("st_distance", &signature, &return_type, &fun) +} + +fn func(args: &[ArrayRef]) -> DFResult { + let geo1 = args[0].as_ref(); + let geo2 = args[1].as_ref(); + + let mut builder = Float64Builder::with_capacity(geo1.len()); + + let geo1 = downcast_array::(geo1); + let geo2 = downcast_array::(geo2); + + geo1.iter() + .zip(geo2.iter()) + .try_for_each(|(l, r)| { + match (l, r) { + (None, _) | (_, None) => builder.append_null(), + (Some(l), Some(r)) => { + let wkt_l = WktStr(l); + let wkt_r = WktStr(r); + + let geo_l = wkt_l + .to_geo() + .map_err(|err| DataFusionError::Execution(err.to_string()))?; + let geo_r = wkt_r + .to_geo() + .map_err(|err| DataFusionError::Execution(err.to_string()))?; + + let distance = distance(&geo_l, &geo_r)?; + + builder.append_value(distance) + } + }; + + Ok::<(), DataFusionError>(()) + }) + .map_err(|err| DataFusionError::Execution(err.to_string()))?; + + let result = builder.finish(); + + Ok(Arc::new(result)) +} + +fn distance(geo_l: &Geometry, geo_r: &Geometry) -> DFResult { + let distance = match (geo_l, geo_r) { + (Geometry::Point(p), other) => point_distance(p, other)?, + (Geometry::Line(p), other) => line_distance(p, other)?, + (Geometry::LineString(p), other) => line_string_distance(p, other)?, + (Geometry::Polygon(p), other) => polygon_distance(p, other)?, + (Geometry::MultiPoint(p), other) => multi_point_distance(p, other)?, + (Geometry::MultiLineString(p), other) => multi_line_string_distance(p, other)?, + (Geometry::MultiPolygon(p), other) => multi_polygon_distance(p, other)?, + (Geometry::Triangle(p), other) => triangle_distance(p, other)?, + (geo_l, geo_r) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between {:?} and {:?} is not supported", + geo_l, geo_r + ))); + } + }; + + Ok(distance) +} + +fn point_distance(point: &Point, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(rhs) => point.euclidean_distance(rhs), + Geometry::LineString(rhs) => point.euclidean_distance(rhs), + Geometry::Polygon(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPoint(rhs) => point.euclidean_distance(rhs), + Geometry::MultiLineString(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPolygon(rhs) => point.euclidean_distance(rhs), + Geometry::GeometryCollection(_) | Geometry::Rect(_) | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between POINT and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn line_distance(point: &Line, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(rhs) => point.euclidean_distance(rhs), + Geometry::LineString(rhs) => point.euclidean_distance(rhs), + Geometry::Polygon(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPolygon(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between LINE and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn line_string_distance(point: &LineString, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(rhs) => point.euclidean_distance(rhs), + Geometry::LineString(rhs) => point.euclidean_distance(rhs), + Geometry::Polygon(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between LINESTRING and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn polygon_distance(point: &Polygon, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(rhs) => point.euclidean_distance(rhs), + Geometry::LineString(rhs) => point.euclidean_distance(rhs), + Geometry::Polygon(rhs) => point.euclidean_distance(rhs), + Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between POLYGON and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn multi_polygon_distance(point: &MultiPolygon, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(rhs) => point.euclidean_distance(rhs), + Geometry::LineString(_) + | Geometry::Polygon(_) + | Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between MULTIPOLYGON and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn multi_point_distance(point: &MultiPoint, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(_) + | Geometry::LineString(_) + | Geometry::Polygon(_) + | Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between MULTIPOINT and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn multi_line_string_distance(point: &MultiLineString, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(_) + | Geometry::LineString(_) + | Geometry::Polygon(_) + | Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between MULTILINESTRING and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} + +fn triangle_distance(point: &Triangle, other: &Geometry) -> DFResult { + let dist = match other { + Geometry::Point(rhs) => point.euclidean_distance(rhs), + Geometry::Line(_) + | Geometry::LineString(_) + | Geometry::Polygon(_) + | Geometry::MultiPolygon(_) + | Geometry::MultiPoint(_) + | Geometry::MultiLineString(_) + | Geometry::GeometryCollection(_) + | Geometry::Rect(_) + | Geometry::Triangle(_) => { + return Err(DataFusionError::Execution(format!( + "Calculating the distance between TRIANGLE and {:?} is not supported", + other + ))); + } + }; + + Ok(dist) +} diff --git a/query_server/query/src/extension/expr/scalar_function/mod.rs b/query_server/query/src/extension/expr/scalar_function/mod.rs index 9552189a9..321e0b1b2 100644 --- a/query_server/query/src/extension/expr/scalar_function/mod.rs +++ b/query_server/query/src/extension/expr/scalar_function/mod.rs @@ -3,6 +3,7 @@ mod duration_in; mod example; mod gapfill; mod gauge; +mod gis; mod interpolate; mod locf; mod state_at; @@ -31,6 +32,7 @@ pub fn register_udfs(func_manager: &mut dyn FunctionMetadataManager) -> Result<( gauge::register_udfs(func_manager)?; duration_in::register_udf(func_manager)?; state_at::register_udf(func_manager)?; + gis::register_udfs(func_manager)?; Ok(()) } diff --git a/query_server/sqllogicaltests/cases/function/gis/setup.slt b/query_server/sqllogicaltests/cases/function/gis/setup.slt new file mode 100644 index 000000000..e75749cf9 --- /dev/null +++ b/query_server/sqllogicaltests/cases/function/gis/setup.slt @@ -0,0 +1,29 @@ +########## +## DDL +########## + +statement ok +alter database public set ttl '1000000d'; + +statement ok +drop table if exists gis_loc; + +statement ok +CREATE TABLE IF NOT EXISTS gis_loc(loc geometry(point, 0)); + +########## +## Query +########## + +# prepare data +statement ok +INSERT gis_loc(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'), + ('1999-12-31 00:00:00.005', 'POINT(0 1)'), + ('1999-12-31 00:00:00.010', 'POINT(0 2)'), + ('1999-12-31 00:00:10.015', 'POINT(0 3)'), + ('1999-12-31 00:00:10.020', 'POINT(0 4)'), + ('1999-12-31 00:10:00.025', 'POINT(0 5)'), + ('1999-12-31 00:10:00.030', 'POINT(0 6)'), + ('1999-12-31 01:00:00.035', 'POINT(0 7)'); diff --git a/query_server/sqllogicaltests/cases/function/gis/st_distance.slt b/query_server/sqllogicaltests/cases/function/gis/st_distance.slt new file mode 100644 index 000000000..4c5e10512 --- /dev/null +++ b/query_server/sqllogicaltests/cases/function/gis/st_distance.slt @@ -0,0 +1,191 @@ +include ./setup.slt + +########## +## Query +########## + +query +SELECT time, loc, st_distance('POINT(0 0)', loc) FROM gis_loc order by time, loc; +---- +1999-12-31T00:00:00 POINT(0 0) 0.0 +1999-12-31T00:00:00.005 POINT(0 1) 1.0 +1999-12-31T00:00:00.010 POINT(0 2) 2.0 +1999-12-31T00:00:10.015 POINT(0 3) 3.0 +1999-12-31T00:00:10.020 POINT(0 4) 4.0 +1999-12-31T00:10:00.025 POINT(0 5) 5.0 +1999-12-31T00:10:00.030 POINT(0 6) 6.0 +1999-12-31T01:00:00.035 POINT(0 7) 7.0 + +query +select st_distance('POINT(0 0)', 'POINT(0 0)'); +---- +0.0 + +query +select st_distance('POINT(0 0)', 'LINESTRING (30 10, 10 30, 40 40)'); +---- +28.284271247461902 + +query +select st_distance('POINT(0 0)', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); +---- +22.360679774997898 + +query +select st_distance('POINT(0 0)', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); +---- +22.360679774997898 + +query +select st_distance('POINT(0 0)', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); +---- +28.284271247461902 + +query +select st_distance('POINT(0 0)', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); +---- +28.284271247461902 + +query +select st_distance('POINT(0 0)', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); +---- +14.142135623730951 + +query +select st_distance('POINT(0 0)', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); +---- +11.180339887498949 + +query +select st_distance('POINT(0 0)', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); +---- +14.142135623730951 + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POINT and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('POINT(0 0)', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + + +query +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'LINESTRING (30 10, 10 30, 40 40)'); +---- +0.0 + +query +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); +---- +0.0 + +query +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); +---- +0.0 + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between LINESTRING and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('LINESTRING (30 10, 10 30, 40 40)', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + + +query +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); +---- +0.0 + +query +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); +---- +0.0 + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between POLYGON and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + + + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOINT and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTILINESTRING and MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\) is not supported", .* +select st_distance('MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTILINESTRING and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\) is not supported", .* +select st_distance('MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTILINESTRING and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTILINESTRING and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOLYGON and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\) is not supported", .* +select st_distance('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOLYGON and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOLYGON and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + + + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOLYGON and MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\) is not supported", .* +select st_distance('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between MULTIPOLYGON and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +query error Arrow error: Io error: Status \{ code: Internal, message: "Execute logical plan: Datafusion: Optimizer rule 'simplify_expressions' failed\\ncaused by\\nExecution error: Execution error: Calculating the distance between GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) and GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\) is not supported", .* +select st_distance('GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); From 67df72dcf6ff7bce96632debf71c363083166718 Mon Sep 17 00:00:00 2001 From: "yukkit.zhang" Date: Thu, 31 Aug 2023 14:17:27 +0800 Subject: [PATCH 2/4] minor: fix misspelled words --- query_server/query/src/prom/time_series/writer.rs | 2 +- query_server/query/src/sql/planner.rs | 4 ++-- query_server/spi/src/lib.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/query_server/query/src/prom/time_series/writer.rs b/query_server/query/src/prom/time_series/writer.rs index b139ff9ca..45b0e882f 100644 --- a/query_server/query/src/prom/time_series/writer.rs +++ b/query_server/query/src/prom/time_series/writer.rs @@ -205,7 +205,7 @@ impl WriterBuilder { return Err(QueryError::DataType { data_type: type_.to_string(), column: array.name().to_string(), - promot: "only support data type: string(label)/float(sample value)/timestamp(time)".to_string(), + prompt: "only support data type: string(label)/float(sample value)/timestamp(time)".to_string(), }); } }) diff --git a/query_server/query/src/sql/planner.rs b/query_server/query/src/sql/planner.rs index 50fdf4c16..dd23df9d6 100644 --- a/query_server/query/src/sql/planner.rs +++ b/query_server/query/src/sql/planner.rs @@ -1217,10 +1217,10 @@ impl<'a, S: ContextProviderExtension + Send + Sync + 'a> SqlPlanner<'a, S> { data_type: &SQLDataType, time_unit: TimeUnit, ) -> Result { - let unsupport_type_err = &|promot: String| QueryError::DataType { + let unsupport_type_err = &|prompt: String| QueryError::DataType { column: column_name.to_string(), data_type: data_type.to_string(), - promot, + prompt, }; match data_type { diff --git a/query_server/spi/src/lib.rs b/query_server/spi/src/lib.rs index 7f6b0c20b..1a8291c71 100644 --- a/query_server/spi/src/lib.rs +++ b/query_server/spi/src/lib.rs @@ -292,13 +292,13 @@ pub enum QueryError { "Semantic error: Unexpected data type {} of {}, {}", data_type, column, - promot + prompt ))] #[error_code(code = 41)] DataType { data_type: String, column: String, - promot: String, + prompt: String, }, #[snafu(display( From 253b72e8e2dd09cc630596e65a44aa9aa9870ac3 Mon Sep 17 00:00:00 2001 From: "yukkit.zhang" Date: Thu, 31 Aug 2023 16:49:03 +0800 Subject: [PATCH 3/4] feat: support gis function ST_AsBinary and ST_GeomFromWKB --- .../extension/expr/scalar_function/gis/mod.rs | 4 ++ .../expr/scalar_function/gis/st_asbinary.rs | 44 +++++++++++++++++++ .../scalar_function/gis/st_geomfromwkb.rs | 44 +++++++++++++++++++ .../cases/function/gis/st_asbinary.slt | 35 +++++++++++++++ .../cases/function/gis/st_geomfromwkb.slt | 22 ++++++++++ 5 files changed, 149 insertions(+) create mode 100644 query_server/query/src/extension/expr/scalar_function/gis/st_asbinary.rs create mode 100644 query_server/query/src/extension/expr/scalar_function/gis/st_geomfromwkb.rs create mode 100644 query_server/sqllogicaltests/cases/function/gis/st_asbinary.slt create mode 100644 query_server/sqllogicaltests/cases/function/gis/st_geomfromwkb.slt diff --git a/query_server/query/src/extension/expr/scalar_function/gis/mod.rs b/query_server/query/src/extension/expr/scalar_function/gis/mod.rs index 55d600148..536afe80a 100644 --- a/query_server/query/src/extension/expr/scalar_function/gis/mod.rs +++ b/query_server/query/src/extension/expr/scalar_function/gis/mod.rs @@ -1,10 +1,14 @@ +mod st_asbinary; mod st_distance; +mod st_geomfromwkb; use spi::query::function::FunctionMetadataManager; use spi::Result; pub fn register_udfs(func_manager: &mut dyn FunctionMetadataManager) -> Result<()> { st_distance::register_udf(func_manager)?; + st_geomfromwkb::register_udf(func_manager)?; + st_asbinary::register_udf(func_manager)?; Ok(()) } diff --git a/query_server/query/src/extension/expr/scalar_function/gis/st_asbinary.rs b/query_server/query/src/extension/expr/scalar_function/gis/st_asbinary.rs new file mode 100644 index 000000000..108080818 --- /dev/null +++ b/query_server/query/src/extension/expr/scalar_function/gis/st_asbinary.rs @@ -0,0 +1,44 @@ +use std::sync::Arc; + +use datafusion::arrow::array::{downcast_array, ArrayRef, BinaryArray, StringArray}; +use datafusion::arrow::datatypes::DataType; +use datafusion::common::Result as DFResult; +use datafusion::logical_expr::{ReturnTypeFunction, ScalarUDF, Signature, Volatility}; +use datafusion::physical_plan::functions::make_scalar_function; +use geozero::wkt::WktStr; +use geozero::{CoordDimensions, ToWkb}; +use spi::query::function::FunctionMetadataManager; +use spi::Result; + +pub fn register_udf(func_manager: &mut dyn FunctionMetadataManager) -> Result { + let udf = new(); + func_manager.register_udf(udf.clone())?; + Ok(udf) +} + +fn new() -> ScalarUDF { + let fun = make_scalar_function(func); + + let signature = Signature::exact(vec![DataType::Utf8], Volatility::Immutable); + let return_type: ReturnTypeFunction = Arc::new(move |_| Ok(Arc::new(DataType::Binary))); + + ScalarUDF::new("st_AsBinary", &signature, &return_type, &fun) +} + +fn func(args: &[ArrayRef]) -> DFResult { + let wkt_arr = args[0].as_ref(); + let wkt_arr = downcast_array::(wkt_arr); + + let result: BinaryArray = wkt_arr + .iter() + .map(|opt| { + opt.and_then(|str| { + let wkb = WktStr(str); + // conversion failed to null + wkb.to_wkb(CoordDimensions::xy()).ok() + }) + }) + .collect(); + + Ok(Arc::new(result)) +} diff --git a/query_server/query/src/extension/expr/scalar_function/gis/st_geomfromwkb.rs b/query_server/query/src/extension/expr/scalar_function/gis/st_geomfromwkb.rs new file mode 100644 index 000000000..455862b71 --- /dev/null +++ b/query_server/query/src/extension/expr/scalar_function/gis/st_geomfromwkb.rs @@ -0,0 +1,44 @@ +use std::sync::Arc; + +use datafusion::arrow::array::{downcast_array, ArrayRef, BinaryArray, StringArray}; +use datafusion::arrow::datatypes::DataType; +use datafusion::common::Result as DFResult; +use datafusion::logical_expr::{ReturnTypeFunction, ScalarUDF, Signature, Volatility}; +use datafusion::physical_plan::functions::make_scalar_function; +use geozero::wkb::Wkb; +use geozero::ToWkt; +use spi::query::function::FunctionMetadataManager; +use spi::Result; + +pub fn register_udf(func_manager: &mut dyn FunctionMetadataManager) -> Result { + let udf = new(); + func_manager.register_udf(udf.clone())?; + Ok(udf) +} + +fn new() -> ScalarUDF { + let fun = make_scalar_function(func); + + let signature = Signature::exact(vec![DataType::Binary], Volatility::Immutable); + let return_type: ReturnTypeFunction = Arc::new(move |_| Ok(Arc::new(DataType::Utf8))); + + ScalarUDF::new("st_GeomFromWKB", &signature, &return_type, &fun) +} + +fn func(args: &[ArrayRef]) -> DFResult { + let wkb_arr = args[0].as_ref(); + let wkb_arr = downcast_array::(wkb_arr); + + let result: StringArray = wkb_arr + .iter() + .map(|opt| { + opt.and_then(|bytes| { + let wkb = Wkb(bytes.to_vec()); + // conversion failed to null + wkb.to_wkt().ok() + }) + }) + .collect(); + + Ok(Arc::new(result)) +} diff --git a/query_server/sqllogicaltests/cases/function/gis/st_asbinary.slt b/query_server/sqllogicaltests/cases/function/gis/st_asbinary.slt new file mode 100644 index 000000000..6c5d7e2a9 --- /dev/null +++ b/query_server/sqllogicaltests/cases/function/gis/st_asbinary.slt @@ -0,0 +1,35 @@ +include ./setup.slt + +########## +## Query +########## + +query +SELECT time, loc, st_AsBinary(loc) FROM gis_loc order by time, loc; +---- +1999-12-31T00:00:00 POINT(0 0) 010100000000000000000000000000000000000000 +1999-12-31T00:00:00.005 POINT(0 1) 01010000000000000000000000000000000000f03f +1999-12-31T00:00:00.010 POINT(0 2) 010100000000000000000000000000000000000040 +1999-12-31T00:00:10.015 POINT(0 3) 010100000000000000000000000000000000000840 +1999-12-31T00:00:10.020 POINT(0 4) 010100000000000000000000000000000000001040 +1999-12-31T00:10:00.025 POINT(0 5) 010100000000000000000000000000000000001440 +1999-12-31T00:10:00.030 POINT(0 6) 010100000000000000000000000000000000001840 +1999-12-31T01:00:00.035 POINT(0 7) 010100000000000000000000000000000000001c40 + +query +select st_AsBinary(null); +---- +NULL + +query +select st_AsBinary('POINT(0 0)'); +---- +010100000000000000000000000000000000000000 + +query +select st_AsBinary('POINT(0, 0)'); +---- +NULL + +query error Arrow error: Io error: Status \{ code: Internal, message: "Build logical plan: Datafusion: Error during planning: No function matches the given name and argument types 'st_AsBinary\(Utf8, Null\)'\. You might need to add explicit type casts\.\\n\\tCandidate functions:\\n\\tst_AsBinary\(Utf8\)", .* +select st_AsBinary('POINT(0, 0)', null); diff --git a/query_server/sqllogicaltests/cases/function/gis/st_geomfromwkb.slt b/query_server/sqllogicaltests/cases/function/gis/st_geomfromwkb.slt new file mode 100644 index 000000000..eb1ca3d42 --- /dev/null +++ b/query_server/sqllogicaltests/cases/function/gis/st_geomfromwkb.slt @@ -0,0 +1,22 @@ +query +select ST_GeomFromWKB(st_AsBinary('POINT(0 0)')); +---- +POINT(0 0) + +query +select ST_GeomFromWKB(st_AsBinary('MULTIPOINT (10 40, 40 30, 20 20, 30 10)')); +---- +MULTIPOINT(10 40,40 30,20 20,30 10) + +query +select ST_GeomFromWKB(st_AsBinary('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))')); +---- +MULTIPOLYGON(((30 20,45 40,10 40,30 20)),((15 5,40 10,10 20,5 10,15 5))) + +query +select ST_GeomFromWKB(st_AsBinary('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))')); +---- +MULTIPOLYGON(((40 40,20 45,45 30,40 40)),((20 35,10 30,10 10,30 5,45 20,20 35),(30 20,20 15,20 25,30 20))) + +query error Arrow error: Io error: Status \{ code: Internal, message: "Build logical plan: Datafusion: Error during planning: No function matches the given name and argument types 'st_GeomFromWKB\(Utf8\)'\. You might need to add explicit type casts\.\\n\\tCandidate functions:\\n\\tst_GeomFromWKB\(Binary\)", .* +select ST_GeomFromWKB('invalid'); From 83e19e8515d8ecd4511bc30e55bf78177c7265e0 Mon Sep 17 00:00:00 2001 From: "yukkit.zhang" Date: Mon, 4 Sep 2023 15:52:39 +0800 Subject: [PATCH 4/4] feat: check geometry column data format --- common/models/src/gis/data_type.rs | 40 +- common/models/src/schema.rs | 10 +- query_server/query/src/data_source/mod.rs | 2 + .../src/data_source/sink/obj_store/mod.rs | 7 + .../query/src/data_source/sink/tskv.rs | 5 + .../write_exec_ext/external_table.rs | 1 + .../physical/optimizer_rule/add_assert.rs | 104 +++++ .../extension/physical/optimizer_rule/mod.rs | 1 + .../physical/plan_node/assert/geom_write.rs | 92 ++++ .../physical/plan_node/assert/mod.rs | 159 +++++++ .../src/extension/physical/plan_node/mod.rs | 1 + .../physical/plan_node/table_writer.rs | 4 + .../query/src/sql/physical/planner.rs | 3 + query_server/query/src/sql/planner.rs | 10 +- query_server/spi/src/lib.rs | 6 + .../cases/function/gis/check_write.slt | 426 ++++++++++++++++++ 16 files changed, 853 insertions(+), 18 deletions(-) create mode 100644 query_server/query/src/extension/physical/optimizer_rule/add_assert.rs create mode 100644 query_server/query/src/extension/physical/plan_node/assert/geom_write.rs create mode 100644 query_server/query/src/extension/physical/plan_node/assert/mod.rs create mode 100644 query_server/sqllogicaltests/cases/function/gis/check_write.slt diff --git a/common/models/src/gis/data_type.rs b/common/models/src/gis/data_type.rs index a8d2bf1a5..3407cfac9 100644 --- a/common/models/src/gis/data_type.rs +++ b/common/models/src/gis/data_type.rs @@ -1,4 +1,5 @@ use std::fmt::Display; +use std::str::FromStr; use serde::{Deserialize, Serialize}; @@ -24,24 +25,43 @@ impl Display for Geometry { #[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone, Eq, Hash)] pub enum GeometryType { Point, - Linestring, + LineString, Polygon, - Multipoint, - Multilinestring, - Multipolygon, - Geometrycollection, + MultiPoint, + MultiLineString, + MultiPolygon, + GeometryCollection, } impl Display for GeometryType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Point => write!(f, "POINT"), - Self::Linestring => write!(f, "LINESTRING"), + Self::LineString => write!(f, "LINESTRING"), Self::Polygon => write!(f, "POLYGON"), - Self::Multipoint => write!(f, "MULTIPOINT"), - Self::Multilinestring => write!(f, "MULTILINESTRING"), - Self::Multipolygon => write!(f, "MULTIPOLYGON"), - Self::Geometrycollection => write!(f, "GEOMETRYCOLLECTION"), + Self::MultiPoint => write!(f, "MULTIPOINT"), + Self::MultiLineString => write!(f, "MULTILINESTRING"), + Self::MultiPolygon => write!(f, "MULTIPOLYGON"), + Self::GeometryCollection => write!(f, "GEOMETRYCOLLECTION"), + } + } +} + +impl FromStr for GeometryType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "POINT" => Ok(Self::Point), + "LINESTRING" => Ok(Self::LineString), + "POLYGON" => Ok(Self::Polygon), + "MULTIPOINT" => Ok(Self::MultiPoint), + "MULTILINESTRING" => Ok(Self::MultiLineString), + "MULTIPOLYGON" => Ok(Self::MultiPolygon), + "GEOMETRYCOLLECTION" => Ok(Self::GeometryCollection), + other => { + Err(format!("Invalid geometry type: {}, excepted: POINT | LINESTRING | POLYGON | MULTIPOINT | MULTILINESTRING | MULTIPOLYGON | GEOMETRYCOLLECTION", other)) + } } } } diff --git a/common/models/src/schema.rs b/common/models/src/schema.rs index ffe09548a..7e0baf056 100644 --- a/common/models/src/schema.rs +++ b/common/models/src/schema.rs @@ -372,7 +372,8 @@ pub struct TableColumn { pub encoding: Encoding, } -pub const SRID_META_KEY: &str = "srid"; +pub const GIS_SRID_META_KEY: &str = "gis.srid"; +pub const GIS_SUB_TYPE_META_KEY: &str = "gis.sub_type"; impl From<&TableColumn> for ArrowField { fn from(column: &TableColumn) -> Self { @@ -381,8 +382,11 @@ impl From<&TableColumn> for ArrowField { map.insert(TAG.to_string(), column.column_type.is_tag().to_string()); // 通过 SRID_META_KEY 标记 Geometry 类型的列 - if let ColumnType::Field(ValueType::Geometry(Geometry { srid, .. })) = column.column_type { - map.insert(SRID_META_KEY.to_string(), srid.to_string()); + if let ColumnType::Field(ValueType::Geometry(Geometry { srid, sub_type })) = + column.column_type + { + map.insert(GIS_SUB_TYPE_META_KEY.to_string(), sub_type.to_string()); + map.insert(GIS_SRID_META_KEY.to_string(), srid.to_string()); } let nullable = column.nullable(); diff --git a/query_server/query/src/data_source/mod.rs b/query_server/query/src/data_source/mod.rs index 8bb064a74..25fdf570a 100644 --- a/query_server/query/src/data_source/mod.rs +++ b/query_server/query/src/data_source/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use async_trait::async_trait; +use datafusion::arrow::datatypes::SchemaRef; use datafusion::arrow::record_batch::RecordBatch; use datafusion::common::Result as DFResult; use datafusion::execution::context::{SessionState, TaskContext}; @@ -48,6 +49,7 @@ pub trait RecordBatchSink: Send + Sync { } pub trait RecordBatchSinkProvider: Send + Sync { + fn schema(&self) -> SchemaRef; fn create_batch_sink( &self, context: Arc, diff --git a/query_server/query/src/data_source/sink/obj_store/mod.rs b/query_server/query/src/data_source/sink/obj_store/mod.rs index 86df7542f..0e4385563 100644 --- a/query_server/query/src/data_source/sink/obj_store/mod.rs +++ b/query_server/query/src/data_source/sink/obj_store/mod.rs @@ -62,6 +62,7 @@ pub struct ObjectStoreSinkProvider { object_store: Arc, serializer: Arc, file_extension: String, + schema: SchemaRef, } impl ObjectStoreSinkProvider { @@ -70,17 +71,23 @@ impl ObjectStoreSinkProvider { object_store: Arc, serializer: Arc, file_extension: String, + schema: SchemaRef, ) -> Self { Self { location, object_store, serializer, file_extension, + schema, } } } impl RecordBatchSinkProvider for ObjectStoreSinkProvider { + fn schema(&self) -> SchemaRef { + self.schema.clone() + } + fn create_batch_sink( &self, context: Arc, diff --git a/query_server/query/src/data_source/sink/tskv.rs b/query_server/query/src/data_source/sink/tskv.rs index 7d584f22f..35c07ef51 100644 --- a/query_server/query/src/data_source/sink/tskv.rs +++ b/query_server/query/src/data_source/sink/tskv.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use async_trait::async_trait; use coordinator::service::CoordinatorRef; +use datafusion::arrow::datatypes::SchemaRef; use datafusion::arrow::record_batch::RecordBatch; use datafusion::execution::context::TaskContext; use datafusion::physical_plan::metrics::{self, Count, ExecutionPlanMetricsSet, MetricBuilder}; @@ -77,6 +78,10 @@ impl TskvRecordBatchSinkProvider { } impl RecordBatchSinkProvider for TskvRecordBatchSinkProvider { + fn schema(&self) -> SchemaRef { + self.schema.to_arrow_schema() + } + fn create_batch_sink( &self, context: Arc, diff --git a/query_server/query/src/data_source/write_exec_ext/external_table.rs b/query_server/query/src/data_source/write_exec_ext/external_table.rs index 6e02f46be..abfcdf0d3 100644 --- a/query_server/query/src/data_source/write_exec_ext/external_table.rs +++ b/query_server/query/src/data_source/write_exec_ext/external_table.rs @@ -57,6 +57,7 @@ impl WriteExecExt for ListingTable { object_store, serializer, file_extension, + input.schema(), )); Ok(Arc::new(TableWriterExec::new( diff --git a/query_server/query/src/extension/physical/optimizer_rule/add_assert.rs b/query_server/query/src/extension/physical/optimizer_rule/add_assert.rs new file mode 100644 index 000000000..c91b7edd6 --- /dev/null +++ b/query_server/query/src/extension/physical/optimizer_rule/add_assert.rs @@ -0,0 +1,104 @@ +use std::str::FromStr; +use std::sync::Arc; + +use datafusion::common::tree_node::{Transformed, TreeNode}; +use datafusion::common::Result as DFResult; +use datafusion::config::ConfigOptions; +use datafusion::error::DataFusionError; +use datafusion::physical_optimizer::PhysicalOptimizerRule; +use datafusion::physical_plan::ExecutionPlan; +use models::gis::data_type::GeometryType; +use models::schema::GIS_SUB_TYPE_META_KEY; +use spi::QueryError; + +use crate::extension::physical::plan_node::assert::geom_write::AssertGeomType; +use crate::extension::physical::plan_node::assert::AssertExec; +use crate::extension::physical::plan_node::table_writer::TableWriterExec; +use crate::extension::utils::downcast_execution_plan; + +#[non_exhaustive] +pub struct AddAssertExec {} + +impl AddAssertExec { + pub fn new() -> Self { + Self {} + } +} + +impl Default for AddAssertExec { + fn default() -> Self { + Self::new() + } +} + +impl PhysicalOptimizerRule for AddAssertExec { + fn optimize( + &self, + plan: Arc, + _config: &ConfigOptions, + ) -> DFResult> { + plan.transform_down(&|plan| { + if let Some(exec) = downcast_execution_plan::(plan.as_ref()) { + if let Some(new_child) = add_table_write_asserter_if_necessary(exec)? { + let new_plan = plan.with_new_children(vec![new_child])?; + return Ok(Transformed::Yes(new_plan)); + } + } + + Ok(Transformed::No(plan)) + }) + } + + fn name(&self) -> &str { + "add_assert_exec" + } + + fn schema_check(&self) -> bool { + true + } +} + +fn add_table_write_asserter_if_necessary( + exec: &TableWriterExec, +) -> DFResult>> { + let schema = exec.sink_schema(); + let child = exec.children()[0].clone(); + + let geoms_with_idx = schema + .fields() + .iter() + .enumerate() + .filter_map(|(idx, field)| { + match field + .metadata() + .get(GIS_SUB_TYPE_META_KEY) + .map(|e| GeometryType::from_str(e)) + { + Some(Ok(sub_type)) => { + // The target table for the write operation contains a column of type geometry + Ok(Some((sub_type, idx))) + } + Some(Err(err)) => { + // Contains a column of type geometry, but the type is not recognized + Err(DataFusionError::External(Box::new( + QueryError::InvalidGeometryType { reason: err }, + ))) + } + None => { + // Not contain a column of type geometry + Ok(None) + } + } + .transpose() + }) + .collect::>>()?; + + if geoms_with_idx.is_empty() { + return Ok(None); + } + + let assert_expr = Arc::new(AssertGeomType::new(geoms_with_idx)); + let new_child = Arc::new(AssertExec::new(assert_expr, child)); + + Ok(Some(new_child)) +} diff --git a/query_server/query/src/extension/physical/optimizer_rule/mod.rs b/query_server/query/src/extension/physical/optimizer_rule/mod.rs index f62d264b4..85a321092 100644 --- a/query_server/query/src/extension/physical/optimizer_rule/mod.rs +++ b/query_server/query/src/extension/physical/optimizer_rule/mod.rs @@ -1,3 +1,4 @@ //! physical plan optimizer rule +pub mod add_assert; pub mod add_state_store; pub mod add_traced_proxy; diff --git a/query_server/query/src/extension/physical/plan_node/assert/geom_write.rs b/query_server/query/src/extension/physical/plan_node/assert/geom_write.rs new file mode 100644 index 000000000..83f5cf1a6 --- /dev/null +++ b/query_server/query/src/extension/physical/plan_node/assert/geom_write.rs @@ -0,0 +1,92 @@ +use std::fmt::{Debug, Display}; + +use datafusion::arrow::array::{downcast_array, Array, StringArray}; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::common::Result as DFResult; +use datafusion::error::DataFusionError; +use geo::Geometry; +use geozero::wkt::WktStr; +use geozero::ToGeo; +use models::gis::data_type::GeometryType; +use spi::QueryError; + +use super::AssertExpr; + +#[derive(Debug)] +pub struct AssertGeomType { + geom_with_idx: Vec<(GeometryType, usize)>, +} + +impl AssertGeomType { + pub fn new(geom_with_idx: Vec<(GeometryType, usize)>) -> Self { + Self { geom_with_idx } + } +} + +impl AssertExpr for AssertGeomType { + fn assert(&self, batch: &RecordBatch) -> DFResult<()> { + for (sub_type, idx) in &self.geom_with_idx { + let column = batch.column(*idx).as_ref(); + check_wkt(sub_type, column)?; + } + + Ok(()) + } +} + +impl Display for AssertGeomType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = self + .geom_with_idx + .iter() + .map(|(_, idx)| idx.to_string()) + .collect::>() + .join(", "); + + write!(f, "AssertGeomType({})", str) + } +} + +macro_rules! define_check_wkt { + ($( + $sub_type:ident $(= $string_keyword:expr)? + ),*) => { + fn check_wkt(sub_type: &GeometryType, array: &dyn Array) -> DFResult<()> { + let str_array = downcast_array::(array); + + match sub_type { + $(GeometryType::$sub_type => { + for ele in str_array.iter().flatten() { + let geom = WktStr(ele).to_geo().map_err(|err| { + DataFusionError::External(Box::new(QueryError::InvalidGeometryType { + reason: format!("{}, expect {}, got {:?}", err, stringify!($sub_type), ele), + })) + })?; + + if let Geometry::$sub_type(_) = &geom { + continue; + } + + return Err(DataFusionError::External(Box::new( + QueryError::InvalidGeometryType { + reason: format!("expect {}, got {:?}", stringify!($sub_type), geom), + }, + ))); + } + }),* + } + + Ok(()) + } + }; +} + +define_check_wkt!( + Point, + LineString, + Polygon, + MultiPoint, + MultiLineString, + MultiPolygon, + GeometryCollection +); diff --git a/query_server/query/src/extension/physical/plan_node/assert/mod.rs b/query_server/query/src/extension/physical/plan_node/assert/mod.rs new file mode 100644 index 000000000..96dd0eac8 --- /dev/null +++ b/query_server/query/src/extension/physical/plan_node/assert/mod.rs @@ -0,0 +1,159 @@ +pub mod geom_write; + +use std::any::Any; +use std::fmt::{Debug, Display}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use datafusion::arrow::datatypes::SchemaRef; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::error::Result; +use datafusion::execution::context::TaskContext; +use datafusion::physical_expr::{EquivalenceProperties, PhysicalSortExpr}; +use datafusion::physical_plan::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet}; +use datafusion::physical_plan::{ + DisplayFormatType, ExecutionPlan, Partitioning, RecordBatchStream, SendableRecordBatchStream, + Statistics, +}; +use futures::{Stream, StreamExt}; +use trace::debug; + +pub type AssertExprRef = Arc; + +pub trait AssertExpr: Send + Sync + Display + Debug { + fn assert(&self, batch: &RecordBatch) -> Result<()>; +} + +/// Execution plan for a Expand +#[derive(Debug)] +pub struct AssertExec { + assert_expr: AssertExprRef, + /// The input plan + child: Arc, + + /// Execution metrics + metrics: ExecutionPlanMetricsSet, +} + +impl AssertExec { + pub fn new(assert_expr: AssertExprRef, child: Arc) -> Self { + Self { + assert_expr, + child, + metrics: ExecutionPlanMetricsSet::new(), + } + } +} + +impl ExecutionPlan for AssertExec { + /// Return a reference to Any that can be used for downcasting + fn as_any(&self) -> &dyn Any { + self + } + + /// Get the schema for this execution plan + fn schema(&self) -> SchemaRef { + self.child.schema() + } + + /// Specifies whether this plan generates an infinite stream of records. + /// If the plan does not support pipelining, but it its input(s) are + /// infinite, returns an error to indicate this. + fn unbounded_output(&self, children: &[bool]) -> Result { + self.child.unbounded_output(children) + } + + fn children(&self) -> Vec> { + vec![self.child.clone()] + } + + /// Get the output partitioning of this plan + fn output_partitioning(&self) -> Partitioning { + self.child.output_partitioning() + } + + fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> { + self.child.output_ordering() + } + + fn maintains_input_order(&self) -> Vec { + // tell optimizer this operator doesn't reorder its input + vec![true] + } + + fn equivalence_properties(&self) -> EquivalenceProperties { + self.child.equivalence_properties() + } + + fn with_new_children( + self: Arc, + children: Vec>, + ) -> Result> { + Ok(Arc::new(AssertExec::new( + self.assert_expr.clone(), + children[0].clone(), + ))) + } + + fn execute( + &self, + partition: usize, + context: Arc, + ) -> Result { + debug!( + "Start ExpandExec::execute for partition {} of context session_id {} and task_id {:?}", + partition, + context.session_id(), + context.task_id() + ); + + Ok(Box::pin(AssertStream { + assert_expr: self.assert_expr.clone(), + input: self.child.execute(partition, context)?, + baseline_metrics: BaselineMetrics::new(&self.metrics, partition), + })) + } + + fn fmt_as(&self, _: DisplayFormatType, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "AssertExec: exprs=[{}]", self.assert_expr) + } + + fn metrics(&self) -> Option { + Some(self.metrics.clone_inner()) + } + + fn statistics(&self) -> Statistics { + // TODO stats: compute statistics from assert_expr + Statistics::default() + } +} + +struct AssertStream { + assert_expr: AssertExprRef, + input: SendableRecordBatchStream, + baseline_metrics: BaselineMetrics, +} + +impl Stream for AssertStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let poll = self.input.poll_next_unpin(cx).map(|x| match x { + Some(Ok(batch)) => match self.assert_expr.assert(&batch) { + Ok(_) => Some(Ok(batch)), + Err(err) => Some(Err(err)), + }, + other => other, + }); + + self.baseline_metrics.record_poll(poll) + } +} + +impl RecordBatchStream for AssertStream { + /// Get the schema + fn schema(&self) -> SchemaRef { + self.input.schema() + } +} diff --git a/query_server/query/src/extension/physical/plan_node/mod.rs b/query_server/query/src/extension/physical/plan_node/mod.rs index b49ab4370..538c74cc2 100644 --- a/query_server/query/src/extension/physical/plan_node/mod.rs +++ b/query_server/query/src/extension/physical/plan_node/mod.rs @@ -5,6 +5,7 @@ use datafusion::error::DataFusionError; use datafusion::physical_plan::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, Time}; pub mod aggregate_filter_scan; +pub mod assert; pub mod expand; pub mod gapfill; pub mod state_restore; diff --git a/query_server/query/src/extension/physical/plan_node/table_writer.rs b/query_server/query/src/extension/physical/plan_node/table_writer.rs index a4cdf67de..e762f26dd 100644 --- a/query_server/query/src/extension/physical/plan_node/table_writer.rs +++ b/query_server/query/src/extension/physical/plan_node/table_writer.rs @@ -55,6 +55,10 @@ impl TableWriterExec { schema, } } + + pub fn sink_schema(&self) -> SchemaRef { + self.record_batch_sink_provider.schema() + } } impl Debug for TableWriterExec { diff --git a/query_server/query/src/sql/physical/planner.rs b/query_server/query/src/sql/physical/planner.rs index 5f98fad3c..ecb3612a7 100644 --- a/query_server/query/src/sql/physical/planner.rs +++ b/query_server/query/src/sql/physical/planner.rs @@ -23,6 +23,7 @@ use spi::query::session::SessionCtx; use spi::Result; use super::optimizer::PhysicalOptimizer; +use crate::extension::physical::optimizer_rule::add_assert::AddAssertExec; use crate::extension::physical::transform_rule::expand::ExpandPlanner; use crate::extension::physical::transform_rule::gapfill::GapFillPlanner; use crate::extension::physical::transform_rule::table_writer::TableWriterPlanner; @@ -110,6 +111,8 @@ impl Default for DefaultPhysicalPlanner { // diagnostic error message when this happens. It makes no changes to the // given query plan; i.e. it only acts as a final gatekeeping rule. Arc::new(PipelineChecker::new()), + // CnosDB + Arc::new(AddAssertExec::new()), ]; Self { diff --git a/query_server/query/src/sql/planner.rs b/query_server/query/src/sql/planner.rs index dd23df9d6..5174c3691 100644 --- a/query_server/query/src/sql/planner.rs +++ b/query_server/query/src/sql/planner.rs @@ -2384,12 +2384,12 @@ fn make_geometry_data_type(params: &[String]) -> std::result::Result GeometryType::Point, - "LINESTRING" => GeometryType::Linestring, + "LINESTRING" => GeometryType::LineString, "POLYGON" => GeometryType::Polygon, - "MULTIPOINT" => GeometryType::Multipoint, - "MULTILINESTRING" => GeometryType::Multilinestring, - "MULTIPOLYGON" => GeometryType::Multipolygon, - "GEOMETRYCOLLECTION" => GeometryType::Geometrycollection, + "MULTIPOINT" => GeometryType::MultiPoint, + "MULTILINESTRING" => GeometryType::MultiLineString, + "MULTIPOLYGON" => GeometryType::MultiPolygon, + "GEOMETRYCOLLECTION" => GeometryType::GeometryCollection, _ => { return Err("sub_type must be POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION".to_string()); } diff --git a/query_server/spi/src/lib.rs b/query_server/spi/src/lib.rs index 1a8291c71..93f1e0d7e 100644 --- a/query_server/spi/src/lib.rs +++ b/query_server/spi/src/lib.rs @@ -502,6 +502,12 @@ pub enum QueryError { AnalyzePushedFilter { reason: String, }, + + #[snafu(display("Invalid geometry type, error: {}", reason))] + #[error_code(code = 73)] + InvalidGeometryType { + reason: String, + }, } impl From for QueryError { diff --git a/query_server/sqllogicaltests/cases/function/gis/check_write.slt b/query_server/sqllogicaltests/cases/function/gis/check_write.slt new file mode 100644 index 000000000..5f71c5f40 --- /dev/null +++ b/query_server/sqllogicaltests/cases/function/gis/check_write.slt @@ -0,0 +1,426 @@ +########## +## DDL +########## + +statement ok +alter database public set ttl '1000000d'; + +statement ok +drop table if exists check_write_point; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_point(loc geometry(point, 0)); + +statement ok +drop table if exists check_write_LINESTRING; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_LINESTRING(loc geometry(LINESTRING, 0)); + +statement ok +drop table if exists check_write_POLYGON; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_POLYGON(loc geometry(POLYGON, 0)); + +statement ok +drop table if exists check_write_MULTIPOINT; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_MULTIPOINT(loc geometry(MULTIPOINT, 0)); + +statement ok +drop table if exists check_write_MULTILINESTRING; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_MULTILINESTRING(loc geometry(MULTILINESTRING, 0)); + +statement ok +drop table if exists check_write_MULTIPOLYGON; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_MULTIPOLYGON(loc geometry(MULTIPOLYGON, 0)); + +statement ok +drop table if exists check_write_GEOMETRYCOLLECTION; + +statement ok +CREATE TABLE IF NOT EXISTS check_write_GEOMETRYCOLLECTION(loc geometry(GEOMETRYCOLLECTION, 0)); + +########## +## insert point +########## + +statement ok +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Point, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_point(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert LINESTRING +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement ok +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect LineString, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_LINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert POLYGON +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement ok +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement ok +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect Polygon, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_POLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert MULTIPOINT +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement ok +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement ok +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPoint, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_MULTIPOINT(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert MULTILINESTRING +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement ok +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiLineString, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_MULTILINESTRING(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert MULTIPOLYGON +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement ok +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement ok +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect MultiPolygon, got GeometryCollection\(GeometryCollection\(\[Point\(Point\(Coord \{ x: 40\.0, y: 10\.0 \}\)\), LineString\(LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\)\), Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}\)\]\)\)", .* +INSERT check_write_MULTIPOLYGON(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))'); + +########## +## insert GEOMETRYCOLLECTION +########## + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got Point\(Point\(Coord \{ x: 0\.0, y: 0\.0 \}\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POINT(0 0)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got LineString\(LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'LINESTRING (30 10, 10 30, 40 40)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 10\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\), interiors: \[\] \}\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got Polygon\(Polygon \{ exterior: LineString\(\[Coord \{ x: 35\.0, y: 10\.0 \}, Coord \{ x: 45\.0, y: 45\.0 \}, Coord \{ x: 15\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 35\.0, y: 10\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 20\.0, y: 30\.0 \}, Coord \{ x: 35\.0, y: 35\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 30\.0 \}\]\)\] \}\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got MultiPoint\(MultiPoint\(\[Point\(Coord \{ x: 10\.0, y: 40\.0 \}\), Point\(Coord \{ x: 40\.0, y: 30\.0 \}\), Point\(Coord \{ x: 20\.0, y: 20\.0 \}\), Point\(Coord \{ x: 30\.0, y: 10\.0 \}\)\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got MultiLineString\(MultiLineString\(\[LineString\(\[Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 20\.0, y: 20\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}\]\), LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 20\.0 \}, Coord \{ x: 30\.0, y: 10\.0 \}\]\)\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 45\.0, y: 40\.0 \}, Coord \{ x: 10\.0, y: 40\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 15\.0, y: 5\.0 \}, Coord \{ x: 40\.0, y: 10\.0 \}, Coord \{ x: 10\.0, y: 20\.0 \}, Coord \{ x: 5\.0, y: 10\.0 \}, Coord \{ x: 15\.0, y: 5\.0 \}\]\), interiors: \[\] \}\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'); + +statement error Arrow error: Io error: Status \{ code: Internal, message: "Could not chunk result: Invalid geometry type, error: expect GeometryCollection, got MultiPolygon\(MultiPolygon\(\[Polygon \{ exterior: LineString\(\[Coord \{ x: 40\.0, y: 40\.0 \}, Coord \{ x: 20\.0, y: 45\.0 \}, Coord \{ x: 45\.0, y: 30\.0 \}, Coord \{ x: 40\.0, y: 40\.0 \}\]\), interiors: \[\] \}, Polygon \{ exterior: LineString\(\[Coord \{ x: 20\.0, y: 35\.0 \}, Coord \{ x: 10\.0, y: 30\.0 \}, Coord \{ x: 10\.0, y: 10\.0 \}, Coord \{ x: 30\.0, y: 5\.0 \}, Coord \{ x: 45\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 35\.0 \}\]\), interiors: \[LineString\(\[Coord \{ x: 30\.0, y: 20\.0 \}, Coord \{ x: 20\.0, y: 15\.0 \}, Coord \{ x: 20\.0, y: 25\.0 \}, Coord \{ x: 30\.0, y: 20\.0 \}\]\)\] \}\]\)\)", .* +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))'); + +statement ok +INSERT check_write_GEOMETRYCOLLECTION(TIME, loc) +VALUES + ('1999-12-31 00:00:00.000', 'GEOMETRYCOLLECTION (POINT (40 10),LINESTRING (10 10, 20 20, 10 40),POLYGON ((40 40, 20 45, 45 30, 40 40)))');