Skip to content

Commit 81c395b

Browse files
committed
implement geometry tests
1 parent df0f8ea commit 81c395b

File tree

8 files changed

+153
-29
lines changed

8 files changed

+153
-29
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ chrono = [
8080
"sqlx-exasol-impl/chrono",
8181
"sqlx-exasol-macros?/chrono",
8282
]
83-
geo-types = ["sqlx-exasol-impl/geo-types"]
83+
geo-types = ["sqlx-exasol-impl/geo-types", "sqlx-exasol-macros?/geo-types"]
8484
json = ["sqlx/json", "sqlx-exasol-impl/json", "sqlx-exasol-macros?/json"]
8585
rust_decimal = [
8686
"sqlx/rust_decimal",

sqlx-exasol-impl/src/type_checking.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ impl_type_checking!(
5959

6060
#[cfg(feature = "uuid")]
6161
sqlx_exasol::types::Uuid,
62+
63+
#[cfg(feature = "geo-types")]
64+
sqlx_exasol::types::geo_types::Geometry,
6265
},
6366
ParamChecking::Weak,
6467
feature-types: info => info.__type_feature_gate(),

sqlx-exasol-macros/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ migrate = ["sqlx-macros-core/migrate", "sqlx-exasol-impl/migrate"]
2222
# Types
2323
bigdecimal = ["sqlx-macros-core/bigdecimal", "sqlx-exasol-impl/bigdecimal"]
2424
chrono = ["sqlx-macros-core/chrono", "sqlx-exasol-impl/chrono"]
25+
geo-types = ["sqlx-exasol-impl/geo-types"]
26+
json = ["sqlx-macros-core/json", "sqlx-exasol-impl/json"]
2527
rust_decimal = ["sqlx-macros-core/rust_decimal", "sqlx-exasol-impl/rust_decimal"]
2628
time = ["sqlx-macros-core/time", "sqlx-exasol-impl/time"]
2729
uuid = ["sqlx-macros-core/uuid", "sqlx-exasol-impl/uuid"]
28-
json = ["sqlx-macros-core/json", "sqlx-exasol-impl/json"]
2930

3031
[dependencies]
3132
sqlx-macros-core = { workspace = true }

tests/it/compile_time.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,26 @@ test_compile_time_type!(
152152
"INSERT INTO compile_time_tests (column_uuid) VALUES(?);",
153153
"SELECT column_uuid FROM compile_time_tests;"
154154
);
155+
/// This has to be a separate test because Exasol incorrectly reports parameters for `GEOMETRY`
156+
/// columns as `VARCHAR(2000000) UTF8` which is (correctly) mapped to [`String`] by the driver.
157+
///
158+
/// `sqlx` can only map a single concrete type to a column definition, therefore we cannot make
159+
/// [`sqlx_exasol::types::geo_types::Geometry`] implicitly work at compile time as parameters
160+
/// without messing up strings.
161+
///
162+
/// Therefore we'll only test the result set column type.
163+
///
164+
/// See <https://github.com/exasol/websocket-api/issues/39>.
165+
#[cfg(feature = "geo-types")]
166+
#[ignore]
167+
#[sqlx_exasol::test(migrations = "tests/migrations_compile_time")]
168+
async fn test_compile_time_geometry(
169+
mut conn: sqlx_exasol::pool::PoolConnection<sqlx_exasol::Exasol>,
170+
) -> anyhow::Result<()> {
171+
let _: Vec<Option<sqlx_exasol::types::geo_types::Geometry>> =
172+
sqlx_exasol::query_scalar!("SELECT column_geometry FROM compile_time_tests;")
173+
.fetch_all(&mut *conn)
174+
.await?;
175+
176+
Ok(())
177+
}

tests/it/describe.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ CREATE TABLE with_hashtype_and_tinyint (
3333
value_hashtype_1 HASHTYPE(1 BYTE),
3434
value_bool BOOLEAN,
3535
hashtype_n HASHTYPE(8 BYTE),
36-
value_int TINYINT
36+
value_int TINYINT,
37+
geo GEOMETRY
3738
);
3839
",
3940
)
4041
.await?;
4142

4243
let d = conn
43-
.describe("INSERT INTO with_hashtype_and_tinyint VALUES (?, ?, ?, ?, ?);")
44+
.describe("INSERT INTO with_hashtype_and_tinyint VALUES (?, ?, ?, ?, ?, ?);")
4445
.await?;
4546

4647
let parameters = d.parameters().unwrap().unwrap_left();
@@ -50,6 +51,9 @@ CREATE TABLE with_hashtype_and_tinyint (
5051
assert_eq!(parameters[2].name(), "BOOLEAN");
5152
assert_eq!(parameters[3].name(), "HASHTYPE(8 BYTE)");
5253
assert_eq!(parameters[4].name(), "DECIMAL(3, 0)");
54+
// Undocumented inconsistent behavior.
55+
// See <https://github.com/exasol/websocket-api/issues/39>.
56+
assert_eq!(parameters[5].name(), "VARCHAR(2000000) UTF8");
5357

5458
Ok(())
5559
}
@@ -63,7 +67,8 @@ CREATE TABLE with_hashtype_and_tinyint (
6367
value_hashtype_1 HASHTYPE(1 BYTE),
6468
value_bool BOOLEAN,
6569
hashtype_n HASHTYPE(8 BYTE),
66-
value_int TINYINT
70+
value_int TINYINT,
71+
geo GEOMETRY
6772
);
6873
",
6974
)
@@ -80,18 +85,21 @@ CREATE TABLE with_hashtype_and_tinyint (
8085
assert_eq!(d.columns()[2].name(), "value_bool");
8186
assert_eq!(d.columns()[3].name(), "hashtype_n");
8287
assert_eq!(d.columns()[4].name(), "value_int");
88+
assert_eq!(d.columns()[5].name(), "geo");
8389

8490
assert_eq!(d.nullable(0), None);
8591
assert_eq!(d.nullable(1), None);
8692
assert_eq!(d.nullable(2), None);
8793
assert_eq!(d.nullable(3), None);
8894
assert_eq!(d.nullable(4), None);
95+
assert_eq!(d.nullable(5), None);
8996

9097
assert_eq!(d.columns()[0].type_info().name(), "DECIMAL(18, 0)");
9198
assert_eq!(d.columns()[1].type_info().name(), "HASHTYPE(1 BYTE)");
9299
assert_eq!(d.columns()[2].type_info().name(), "BOOLEAN");
93100
assert_eq!(d.columns()[3].type_info().name(), "HASHTYPE(8 BYTE)");
94101
assert_eq!(d.columns()[4].type_info().name(), "DECIMAL(3, 0)");
102+
assert_eq!(d.columns()[5].type_info().name(), "GEOMETRY(0)");
95103

96104
let parameters = d.parameters().unwrap().unwrap_left();
97105

tests/it/geometry.rs

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,110 @@
1-
use sqlx_exasol::types::geo_types::{Geometry, Line, LineString, Point};
1+
use sqlx::types::geo_types::{GeometryCollection, Rect, Triangle};
2+
use sqlx_exasol::types::geo_types::{
3+
Geometry, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
4+
};
25

36
use crate::{test_type_array, test_type_valid};
47

5-
// TODO: Write EMPTY types tests too
8+
test_type_valid!(geometry_point<Geometry>::"GEOMETRY"::(
9+
"'POINT (1 2)'" => Geometry::Point(Point::new(1.0, 2.0)),
10+
// This is how [`geo-types`] represents an empty [`Point`].
11+
"'POINT EMPTY'" => Geometry::MultiPoint(MultiPoint::<f64>::empty())
12+
));
13+
test_type_valid!(geometry_linestring<Geometry>::"GEOMETRY"::(
14+
"'LINESTRING (1 2, 3 4)'" =>
15+
Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])),
16+
"'LINESTRING EMPTY'" =>
17+
Geometry::LineString(LineString::<f64>::empty()),
18+
"'LINEARRING (1 2, 3 4, 5 6, 1 2)'" =>
19+
Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into(), (5.0, 6.0).into(), (1.0, 2.0).into()])),
20+
"'LINEARRING EMPTY'" =>
21+
Geometry::LineString(LineString::<f64>::empty())
22+
));
23+
test_type_valid!(geometry_polygon<Geometry>::"GEOMETRY"::(
24+
"'POLYGON((1 2, 3 4, 5 6, 1 2))'" =>
25+
Geometry::Polygon(Polygon::new(
26+
LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into(), (5.0, 6.0).into()]),
27+
Vec::new())
28+
),
29+
"'POLYGON((5 1, 5 5, 10 5, 5 1), (6 2, 6 3, 7 3, 7 2, 6 2))'" =>
30+
Geometry::Polygon(Polygon::new(
31+
LineString(vec![(5.0, 1.0).into(), (5.0, 5.0).into(), (10.0, 5.0).into()]),
32+
vec![LineString(vec![(6.0, 2.0).into(), (6.0, 3.0).into(), (7.0, 3.0).into(), (7.0, 2.0).into()])])),
33+
"'POLYGON EMPTY'" =>
34+
Geometry::Polygon(Polygon::<f64>::empty())
35+
));
636

7-
test_type_valid!(geometry_point<Geometry>::"GEOMETRY"::("'POINT (1 2)'" => Geometry::Point(Point::new(1.0, 2.0))));
8-
test_type_valid!(geometry_line<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" => Geometry::Line(Line::new((1.0, 2.0), (3.0, 4.0)))));
9-
test_type_valid!(geometry_linestring<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" => Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()]))));
10-
// test_type_valid!(geometry_linearring<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
11-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
12-
// (geometry_polygon<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
13-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
14-
// (geometry_multilinestring<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
15-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
16-
// (geometry_multipoint<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
17-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
18-
// (geometry_multipolygon<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
19-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
20-
// (geometry_geometry_collection<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
21-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
22-
// (geometry_rect<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
23-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()])))); test_type_valid!
24-
// (geometry_triangle<Geometry>::"GEOMETRY"::("'LINESTRING (1 2,3 4)'" =>
25-
// Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()]))));
37+
test_type_valid!(geometry_multilinestring<Geometry>::"GEOMETRY"::(
38+
"'MULTILINESTRING ((1 2, 3 4), (5 6, 7 8))'" =>
39+
Geometry::MultiLineString(MultiLineString::new(vec![
40+
LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()]),
41+
LineString(vec![(5.0, 6.0).into(), (7.0, 8.0).into()])])),
42+
"'MULTILINESTRING EMPTY'" =>
43+
Geometry::MultiLineString(MultiLineString::<f64>::empty())
44+
));
45+
test_type_valid!(geometry_multipoint<Geometry>::"GEOMETRY"::(
46+
"'MULTIPOINT (1 2, 3 4)'" =>
47+
Geometry::MultiPoint(MultiPoint::new(vec![Point::new(1.0, 2.0), Point::new(3.0, 4.0)])),
48+
"'MULTIPOINT EMPTY'" =>
49+
Geometry::MultiPoint(MultiPoint::<f64>::empty())
50+
));
51+
test_type_valid!(geometry_multipolygon<Geometry>::"GEOMETRY"::(
52+
"'MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2)), ((5 1, 5 5, 10 5, 5 1), (6 2, 6 3, 7 3, 7 2, 6 2)))'" =>
53+
Geometry::MultiPolygon(MultiPolygon::new(vec![
54+
Polygon::new(
55+
LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into(), (5.0, 6.0).into()]),
56+
Vec::new()
57+
),
58+
Polygon::new(
59+
LineString(vec![(5.0, 1.0).into(), (5.0, 5.0).into(), (10.0, 5.0).into()]),
60+
vec![LineString(vec![(6.0, 2.0).into(), (6.0, 3.0).into(), (7.0, 3.0).into(), (7.0, 2.0).into()])]
61+
)])),
62+
"'MULTIPOLYGON EMPTY'" =>
63+
Geometry::MultiPolygon(MultiPolygon::<f64>::empty())
64+
));
65+
test_type_valid!(geometry_geometry_collection<Geometry>::"GEOMETRY"::(
66+
"'GEOMETRYCOLLECTION (POINT(1 2), LINESTRING(1 2, 3 4))'" =>
67+
Geometry::GeometryCollection(GeometryCollection(vec![
68+
Geometry::Point(Point::new(1.0, 2.0)),
69+
Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()]))])),
70+
"'GEOMETRYCOLLECTION EMPTY'" =>
71+
Geometry::GeometryCollection(GeometryCollection::<f64>::empty())
72+
));
73+
74+
test_type_valid!(geometry_linestring_special<Geometry>::"GEOMETRY"::(
75+
"'LINESTRING (1 2,3 4)'" =>
76+
Geometry::Line(Line::new((1.0, 2.0), (3.0, 4.0))) =>
77+
Geometry::LineString(LineString(vec![(1.0, 2.0).into(), (3.0, 4.0).into()]))
78+
));
79+
test_type_valid!(geometry_polygon_special<Geometry>::"GEOMETRY"::(
80+
"'POLYGON ((5 1, 5 5, 10 5, 10 1, 5 1))'" =>
81+
Geometry::Rect(Rect::new((5.0, 5.0), (10.0, 1.0))) =>
82+
Geometry::Polygon(Polygon::new(
83+
LineString(vec![
84+
(5.0, 1.0).into(),
85+
(5.0, 5.0).into(),
86+
(10.0, 5.0).into(),
87+
(10.0, 1.0).into(),
88+
]),
89+
Vec::new()))
90+
));
91+
92+
// Defined in Counter-ClockWise order (CCW).
93+
test_type_valid!(geometry_triangle<Geometry>::"GEOMETRY"::(
94+
"'POLYGON ((10 1, 10 5, 5 1, 10 1))'" =>
95+
Geometry::Triangle(Triangle::new(
96+
(10.0, 1.0).into(),
97+
(10.0, 5.0).into(),
98+
(5.0, 1.0).into(),
99+
)) =>
100+
Geometry::Polygon(Polygon::new(
101+
LineString(vec![
102+
(10.0, 1.0).into(),
103+
(10.0, 5.0).into(),
104+
(5.0, 1.0).into(),
105+
]),
106+
Vec::new()))
107+
));
26108

27109
test_type_valid!(geometry_option<Option<Geometry>>::"GEOMETRY"::("''" => None::<Geometry>, "NULL" => None::<Geometry>, "'POINT (1 2)'" => Some(Geometry::Point(Point::new(1.0, 2.0)))));
28110

tests/it/invalid.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ test_type_invalid!(utf8_in_ascii<String>::"VARCHAR(100) ASCII"::("first value
4747
test_type_invalid!(interval_ytm_str<String>::"INTERVAL YEAR TO MONTH"::("+01-05"));
4848
test_type_invalid!(interval_dts_str<String>::"INTERVAL DAY TO SECOND"::("+10 20:45:50.123"));
4949

50+
// Test using strings with geometry type.
51+
test_type_invalid!(geometry_str<String>::"GEOMETRY"::("POINT (1 2)"));
52+
5053
test_type_invalid!(hashtype_into_small_hashtype<sqlx_exasol::types::HashType>::"HASHTYPE(10 BYTE)"::(sqlx_exasol::types::HashType(String::from("550e8400e29b11d4a716446655440000"))));
5154
test_type_invalid!(hashtype_into_large_hashtype<sqlx_exasol::types::HashType>::"HASHTYPE(20 BYTE)"::(sqlx_exasol::types::HashType(String::from("550e8400e29b11d4a716446655440000"))));
5255

tests/it/macros.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#[macro_export]
44
macro_rules! test_type_valid {
5-
($name:ident<$ty:ty>::$datatype:literal::($($unprepared:expr => $prepared:expr),+)) => {
5+
($name:ident<$ty:ty>::$datatype:literal::($($unprepared:expr => $prepared:expr => $expected:expr),+)) => {
66
paste::item! {
77
#[sqlx_exasol::test]
88
async fn [< test_type_valid_ $name >] (
@@ -33,8 +33,8 @@ macro_rules! test_type_valid {
3333
let second_value = values.pop().unwrap();
3434

3535
assert_eq!(first_value, second_value, "prepared and unprepared types");
36-
assert_eq!(first_value, $prepared, "provided and expected values");
37-
assert_eq!(second_value, $prepared, "provided and expected values");
36+
assert_eq!(first_value, $expected, "provided and expected values");
37+
assert_eq!(second_value, $expected, "provided and expected values");
3838

3939
con.execute("DELETE FROM sqlx_test_type;").await?;
4040
)+
@@ -43,6 +43,10 @@ macro_rules! test_type_valid {
4343
}
4444
}
4545
};
46+
47+
($name:ident<$ty:ty>::$datatype:literal::($($unprepared:expr => $prepared:expr),+)) => {
48+
$crate::test_type_valid!($name<$ty>::$datatype::($($unprepared => $prepared => $prepared),+));
49+
};
4650

4751
($name:ident<$ty:ty>::$datatype:literal::($($unprepared:expr),+)) => {
4852
$crate::test_type_valid!($name<$ty>::$datatype::($($unprepared => $unprepared),+));

0 commit comments

Comments
 (0)