postgis_butmaintained/
postgis.rs

1//
2// Copyright (c) ShuYu Wang <andelf@gmail.com>, Feather Workshop and Pirmin
3// Kalberer. All rights reserved.
4//
5
6use crate::{
7	ewkb::{
8		self, AsEwkbGeometry, AsEwkbGeometryCollection, AsEwkbLineString, AsEwkbMultiLineString,
9		AsEwkbMultiPoint, AsEwkbMultiPolygon, AsEwkbPoint, AsEwkbPolygon, EwkbRead, EwkbWrite,
10	},
11	twkb::{self, TwkbGeom},
12	types::{LineString, Point, Polygon},
13};
14use bytes::{BufMut, BytesMut};
15use postgres_types::{FromSql, IsNull, ToSql, Type, accepts, to_sql_checked};
16use std::{error::Error, io::Cursor};
17
18macro_rules! accepts_geography {
19	() => {
20		fn accepts(ty: &Type) -> bool {
21			match ty.name() {
22				"geography" | "geometry" => true,
23				_ => false,
24			}
25		}
26	};
27}
28
29impl ToSql for ewkb::EwkbPoint<'_> {
30	accepts_geography!();
31
32	to_sql_checked!();
33
34	fn to_sql(&self, _: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
35		self.write_ewkb(&mut out.writer())?;
36		Ok(IsNull::No)
37	}
38}
39
40macro_rules! impl_sql_for_point_type {
41	($ptype:ident) => {
42		impl<'a> FromSql<'a> for ewkb::$ptype {
43			accepts_geography!();
44
45			fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
46				let mut rdr = Cursor::new(raw);
47				ewkb::$ptype::read_ewkb(&mut rdr)
48					.map_err(|_| format!("cannot convert {} to {}", ty, stringify!($ptype)).into())
49			}
50		}
51
52		impl ToSql for ewkb::$ptype {
53			to_sql_checked!();
54
55			accepts_geography!();
56
57			fn to_sql(
58				&self,
59				_: &Type,
60				out: &mut BytesMut,
61			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
62				self.as_ewkb().write_ewkb(&mut out.writer())?;
63				Ok(IsNull::No)
64			}
65		}
66	};
67}
68
69impl_sql_for_point_type!(Point);
70impl_sql_for_point_type!(PointZ);
71impl_sql_for_point_type!(PointM);
72impl_sql_for_point_type!(PointZM);
73
74macro_rules! impl_sql_for_geom_type {
75	($geotype:ident) => {
76		impl<'a, T> FromSql<'a> for ewkb::$geotype<T>
77		where
78			T: 'a + Point + EwkbRead,
79		{
80			accepts_geography!();
81
82			fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
83				let mut rdr = Cursor::new(raw);
84				ewkb::$geotype::<T>::read_ewkb(&mut rdr).map_err(|_| {
85					format!("cannot convert {} to {}", ty, stringify!($geotype)).into()
86				})
87			}
88		}
89
90		impl<'a, T> ToSql for ewkb::$geotype<T>
91		where
92			T: 'a + Point + EwkbRead,
93		{
94			to_sql_checked!();
95
96			accepts_geography!();
97
98			fn to_sql(
99				&self,
100				_: &Type,
101				out: &mut BytesMut,
102			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
103				self.as_ewkb().write_ewkb(&mut out.writer())?;
104				Ok(IsNull::No)
105			}
106		}
107	};
108}
109
110impl_sql_for_geom_type!(LineStringT);
111impl_sql_for_geom_type!(PolygonT);
112impl_sql_for_geom_type!(MultiPointT);
113impl_sql_for_geom_type!(MultiLineStringT);
114impl_sql_for_geom_type!(MultiPolygonT);
115
116macro_rules! impl_sql_for_ewkb_type {
117	($ewkbtype:ident contains points) => {
118		impl<'a, T, I> ToSql for ewkb::$ewkbtype<'a, T, I>
119		where
120			T: 'a + Point,
121			I: 'a + Iterator<Item = &'a T> + ExactSizeIterator<Item = &'a T>,
122		{
123			to_sql_checked!();
124
125			accepts_geography!();
126
127			fn to_sql(
128				&self,
129				_: &Type,
130				out: &mut BytesMut,
131			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
132				self.write_ewkb(&mut out.writer())?;
133				Ok(IsNull::No)
134			}
135		}
136	};
137	($ewkbtype:ident contains $itemtypetrait:ident) => {
138		impl<'a, P, I, T, J> ToSql for ewkb::$ewkbtype<'a, P, I, T, J>
139		where
140			P: 'a + Point,
141			I: 'a + Iterator<Item = &'a P> + ExactSizeIterator<Item = &'a P>,
142			T: 'a + $itemtypetrait<'a, ItemType = P, Iter = I>,
143			J: 'a + Iterator<Item = &'a T> + ExactSizeIterator<Item = &'a T>,
144		{
145			to_sql_checked!();
146
147			accepts_geography!();
148
149			fn to_sql(
150				&self,
151				_: &Type,
152				out: &mut BytesMut,
153			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
154				self.write_ewkb(&mut out.writer())?;
155				Ok(IsNull::No)
156			}
157		}
158	};
159	(multipoly $ewkbtype:ident contains $itemtypetrait:ident) => {
160		impl<'a, P, I, L, K, T, J> ToSql for ewkb::$ewkbtype<'a, P, I, L, K, T, J>
161		where
162			P: 'a + Point,
163			I: 'a + Iterator<Item = &'a P> + ExactSizeIterator<Item = &'a P>,
164			L: 'a + LineString<'a, ItemType = P, Iter = I>,
165			K: 'a + Iterator<Item = &'a L> + ExactSizeIterator<Item = &'a L>,
166			T: 'a + $itemtypetrait<'a, ItemType = L, Iter = K>,
167			J: 'a + Iterator<Item = &'a T> + ExactSizeIterator<Item = &'a T>,
168		{
169			to_sql_checked!();
170
171			accepts_geography!();
172
173			fn to_sql(
174				&self,
175				_: &Type,
176				out: &mut BytesMut,
177			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
178				self.write_ewkb(&mut out.writer())?;
179				Ok(IsNull::No)
180			}
181		}
182	};
183}
184
185impl_sql_for_ewkb_type!(EwkbLineString contains points);
186impl_sql_for_ewkb_type!(EwkbPolygon contains LineString);
187impl_sql_for_ewkb_type!(EwkbMultiPoint contains points);
188impl_sql_for_ewkb_type!(EwkbMultiLineString contains LineString);
189impl_sql_for_ewkb_type!(multipoly EwkbMultiPolygon contains Polygon);
190
191impl<P> FromSql<'_> for ewkb::GeometryT<P>
192where
193	P: Point + EwkbRead,
194{
195	accepts_geography!();
196
197	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
198		let mut rdr = Cursor::new(raw);
199		ewkb::GeometryT::<P>::read_ewkb(&mut rdr)
200			.map_err(|_| format!("cannot convert {} to {}", ty, stringify!(P)).into())
201	}
202}
203
204// NOTE: Implement once per point type because AsEwkbPoint<'a> doesn't live long
205// enough for ToSql
206macro_rules! impl_geometry_to_sql {
207	($ptype:path) => {
208		impl ToSql for ewkb::GeometryT<$ptype> {
209			to_sql_checked!();
210
211			accepts_geography!();
212
213			fn to_sql(
214				&self,
215				_: &Type,
216				out: &mut BytesMut,
217			) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
218				self.as_ewkb().write_ewkb(&mut out.writer())?;
219				Ok(IsNull::No)
220			}
221		}
222	};
223}
224
225impl_geometry_to_sql!(ewkb::Point);
226impl_geometry_to_sql!(ewkb::PointZ);
227impl_geometry_to_sql!(ewkb::PointM);
228impl_geometry_to_sql!(ewkb::PointZM);
229
230impl<P> FromSql<'_> for ewkb::GeometryCollectionT<P>
231where
232	P: Point + EwkbRead,
233{
234	accepts_geography!();
235
236	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
237		let mut rdr = Cursor::new(raw);
238		ewkb::GeometryCollectionT::<P>::read_ewkb(&mut rdr)
239			.map_err(|_| format!("cannot convert {} to {}", ty, stringify!(P)).into())
240	}
241}
242
243impl<P> ToSql for ewkb::GeometryCollectionT<P>
244where
245	P: Point + EwkbRead,
246{
247	to_sql_checked!();
248
249	accepts_geography!();
250
251	fn to_sql(&self, _: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
252		self.as_ewkb().write_ewkb(&mut out.writer())?;
253		Ok(IsNull::No)
254	}
255}
256
257// --- TWKB ---
258
259impl FromSql<'_> for twkb::Point {
260	accepts!(BYTEA);
261
262	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
263		let mut rdr = Cursor::new(raw);
264		twkb::Point::read_twkb(&mut rdr)
265			.map_err(|_| format!("cannot convert {} to Point", ty).into())
266	}
267}
268
269impl FromSql<'_> for twkb::LineString {
270	accepts!(BYTEA);
271
272	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
273		let mut rdr = Cursor::new(raw);
274		twkb::LineString::read_twkb(&mut rdr)
275			.map_err(|_| format!("cannot convert {} to LineString", ty).into())
276	}
277}
278
279impl FromSql<'_> for twkb::Polygon {
280	accepts!(BYTEA);
281
282	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
283		let mut rdr = Cursor::new(raw);
284		twkb::Polygon::read_twkb(&mut rdr)
285			.map_err(|_| format!("cannot convert {} to Polygon", ty).into())
286	}
287}
288
289impl FromSql<'_> for twkb::MultiPoint {
290	accepts!(BYTEA);
291
292	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
293		let mut rdr = Cursor::new(raw);
294		twkb::MultiPoint::read_twkb(&mut rdr)
295			.map_err(|_| format!("cannot convert {} to MultiPoint", ty).into())
296	}
297}
298
299impl FromSql<'_> for twkb::MultiLineString {
300	accepts!(BYTEA);
301
302	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
303		let mut rdr = Cursor::new(raw);
304		twkb::MultiLineString::read_twkb(&mut rdr)
305			.map_err(|_| format!("cannot convert {} to MultiLineString", ty).into())
306	}
307}
308
309impl FromSql<'_> for twkb::MultiPolygon {
310	accepts!(BYTEA);
311
312	fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
313		let mut rdr = Cursor::new(raw);
314		twkb::MultiPolygon::read_twkb(&mut rdr)
315			.map_err(|_| format!("cannot convert {} to MultiPolygon", ty).into())
316	}
317}
318
319#[cfg(test)]
320mod tests {
321	use crate::{
322		ewkb::{self, AsEwkbLineString, AsEwkbPoint},
323		twkb, types as postgis,
324	};
325	use postgres::{Client, NoTls};
326	use std::env;
327
328	macro_rules! or_panic {
329		($e:expr) => {
330			match $e {
331				Ok(ok) => ok,
332				Err(err) => panic!("{:#?}", err),
333			}
334		};
335	}
336
337	fn connect() -> Client {
338		match env::var("DBCONN") {
339			Result::Ok(val) => Client::connect(&val as &str, NoTls),
340			Result::Err(err) => panic!("{:#?}", err),
341		}
342		.unwrap()
343	}
344
345	#[test]
346    #[ignore]
347    #[rustfmt::skip]
348    fn test_insert_point() {
349        let mut client = connect();
350        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(Point))", &[]));
351
352        // 'POINT (10 -20)'
353        let point = ewkb::Point::new(10.0, -20.0, None);
354        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&point]));
355        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('POINT(10 -20)') FROM geomtests", &[]));
356        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
357        or_panic!(client.execute("TRUNCATE geomtests", &[]));
358
359        // With SRID
360        let point = ewkb::Point::new(10.0, -20.0, Some(4326));
361        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&point]));
362        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;POINT(10 -20)') FROM geomtests", &[]));
363        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
364        or_panic!(client.execute("TRUNCATE geomtests", &[]));
365
366        let mut client = connect();
367        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(Point, 4326))", &[]));
368
369        let point = ewkb::Point::new(10.0, -20.0, Some(4326));
370        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&point]));
371        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;POINT(10 -20)') FROM geomtests", &[]));
372        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
373        or_panic!(client.execute("TRUNCATE geomtests", &[]));
374
375        // Missing SRID
376        let point = ewkb::Point::new(10.0, -20.0, None);
377        let result = client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&point]);
378        assert!(format!("{}", result.err().unwrap()).starts_with("db error"));
379    }
380
381	#[test]
382    #[ignore]
383    #[rustfmt::skip]
384    fn test_insert_line() {
385        let mut client = connect();
386        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(LineString))", &[]));
387
388        let p = |x, y| ewkb::Point::new(x, y, None);
389        // 'LINESTRING (10 -20, -0 -0.5)'
390        let line = ewkb::LineString {srid: None, points: vec![p(10.0, -20.0), p(0., -0.5)]};
391        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&line]));
392        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('LINESTRING(10 -20, 0 -0.5)') FROM geomtests", &[]));
393        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
394        or_panic!(client.execute("TRUNCATE geomtests", &[]));
395
396        let mut client = connect();
397        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(LineString, 4326))", &[]));
398
399        // 'SRID=4326;LINESTRING (10 -20, -0 -0.5)'
400        let line = ewkb::LineString {srid: Some(4326), points: vec![p(10.0, -20.0), p(0., -0.5)]};
401        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&line]));
402        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;LINESTRING(10 -20, 0 -0.5)') FROM geomtests", &[]));
403        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
404        or_panic!(client.execute("TRUNCATE geomtests", &[]));
405
406        let mut client = connect();
407        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(LineStringZ, 4326))", &[]));
408
409        let p = |x, y, z| ewkb::PointZ { x, y, z, srid: Some(4326) };
410        // 'SRID=4326;LINESTRING (10 -20 100, -0 -0.5 101)'
411        let line = ewkb::LineStringZ {srid: Some(4326), points: vec![p(10.0, -20.0, 100.0), p(0., -0.5, 101.0)]};
412        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&line]));
413        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;LINESTRING (10 -20 100, 0 -0.5 101)') FROM geomtests", &[]));
414        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
415        or_panic!(client.execute("TRUNCATE geomtests", &[]));
416    }
417
418	#[test]
419    #[ignore]
420    #[rustfmt::skip]
421    fn test_insert_polygon() {
422        let mut client = connect();
423        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(Polygon))", &[]));
424        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
425        // SELECT 'SRID=4326;POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry
426        let line = ewkb::LineString {srid: Some(4326), points: vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]};
427        let poly = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
428        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&poly]));
429        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))') FROM geomtests", &[]));
430        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
431    }
432
433	#[test]
434    #[ignore]
435    #[rustfmt::skip]
436    fn test_insert_multipoint() {
437        let mut client = connect();
438        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(MultiPointZ))", &[]));
439        let p = |x, y, z| ewkb::PointZ { x, y, z, srid: Some(4326) };
440        // SELECT 'SRID=4326;MULTIPOINT ((10 -20 100), (0 -0.5 101))'::geometry
441        let points = ewkb::MultiPointZ {srid: Some(4326), points: vec![p(10.0, -20.0, 100.0), p(0., -0.5, 101.0)]};
442        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&points]));
443        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;MULTIPOINT ((10 -20 100), (0 -0.5 101))') FROM geomtests", &[]));
444        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
445    }
446
447	#[test]
448    #[ignore]
449    #[rustfmt::skip]
450    fn test_insert_multiline() {
451        let mut client = connect();
452        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(MultiLineString))", &[]));
453        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
454        // SELECT 'SRID=4326;MULTILINESTRING ((10 -20, 0 -0.5), (0 0, 2 0))'::geometry
455        let line1 = ewkb::LineString {srid: Some(4326), points: vec![p(10.0, -20.0), p(0., -0.5)]};
456        let line2 = ewkb::LineString {srid: Some(4326), points: vec![p(0., 0.), p(2., 0.)]};
457        let multiline = ewkb::MultiLineString {srid: Some(4326),lines: vec![line1, line2]};
458        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&multiline]));
459        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;MULTILINESTRING ((10 -20, 0 -0.5), (0 0, 2 0))') FROM geomtests", &[]));
460        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
461    }
462
463	#[test]
464    #[ignore]
465    #[rustfmt::skip]
466    fn test_insert_multipolygon() {
467        let mut client = connect();
468        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(MultiPolygon))", &[]));
469        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
470        // SELECT 'SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))'::geometry
471        let line = ewkb::LineString {srid: Some(4326), points: vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]};
472        let poly1 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
473        let line = ewkb::LineString {srid: Some(4326), points: vec![p(10., 10.), p(-2., 10.), p(-2., -2.), p(10., -2.), p(10., 10.)]};
474        let poly2 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
475        let multipoly = ewkb::MultiPolygon {srid: Some(4326), polygons: vec![poly1, poly2]};
476        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&multipoly]));
477        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))') FROM geomtests", &[]));
478        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
479    }
480
481	#[test]
482    #[ignore]
483    #[rustfmt::skip]
484    fn test_insert_geometry() {
485        let mut client = connect();
486        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry)", &[]));
487        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
488        // SELECT 'SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))'::geometry
489        let multipoly = {
490            let line = ewkb::LineString {srid: Some(4326), points: vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]};
491            let poly1 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
492            let line = ewkb::LineString {srid: Some(4326), points: vec![p(10., 10.), p(-2., 10.), p(-2., -2.), p(10., -2.), p(10., 10.)]};
493            let poly2 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
494            ewkb::MultiPolygon {srid: Some(4326), polygons: vec![poly1, poly2]}
495        };
496        let geometry = ewkb::GeometryT::MultiPolygon(multipoly);
497        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&geometry]));
498        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))') FROM geomtests", &[]));
499        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
500    }
501
502	#[test]
503    #[ignore]
504    #[rustfmt::skip]
505    fn test_insert_geometrycollection() {
506        let mut client = connect();
507        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(GeometryCollection))", &[]));
508        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
509        // SELECT 'SRID=4326;LINESTRING (10 -20, -0 -0.5)'
510        let line = ewkb::LineString {srid: Some(4326), points: vec![p(10.0, -20.0), p(0., -0.5)]};
511        // SELECT 'SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))'::geometry
512        let multipoly = {
513            let line = ewkb::LineString {srid: Some(4326), points: vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]};
514            let poly1 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
515            let line = ewkb::LineString {srid: Some(4326), points: vec![p(10., 10.), p(-2., 10.), p(-2., -2.), p(10., -2.), p(10., 10.)]};
516            let poly2 = ewkb::Polygon {srid: Some(4326), rings: vec![line]};
517            ewkb::MultiPolygon {srid: Some(4326), polygons: vec![poly1, poly2]}
518        };
519        // SELECT 'SRID=4326;GEOMETRYCOLLECTION (LINESTRING (10 -20,0 -0.5), MULTIPOLYGON (((0 0,2 0,2 2,0 2,0 0)),((10 10,-2 10,-2 -2,10 -2,10 10))))'::geometry
520        let collection = ewkb::GeometryCollection{
521            srid: Some(4326),
522            geometries: vec![
523                ewkb::GeometryT::LineString(line),
524                ewkb::GeometryT::MultiPolygon(multipoly),
525            ],
526        };
527        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&collection]));
528        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('SRID=4326;GEOMETRYCOLLECTION (LINESTRING (10 -20,0 -0.5), MULTIPOLYGON (((0 0,2 0,2 2,0 2,0 0)),((10 10,-2 10,-2 -2,10 -2,10 10))))') FROM geomtests", &[]));
529        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
530    }
531
532	#[test]
533    #[ignore]
534    #[rustfmt::skip]
535    fn test_select_point() {
536        let mut client = connect();
537        let result = or_panic!(client.query("SELECT ('POINT(10 -20)')::geometry", &[]));
538        let point = result.iter().map(|r| r.get::<_, ewkb::Point>(0)).last().unwrap();
539        let expected = ewkb::Point::new(10.0, -20.0, None);
540        assert_eq!(point.x(), expected.x());
541        assert_eq!(point.y(), expected.y());
542        assert_eq!(point.srid, expected.srid);
543
544        let result = or_panic!(client.query("SELECT 'SRID=4326;POINT(10 -20)'::geometry", &[]));
545        let point = result.iter().map(|r| r.get::<_, ewkb::Point>(0)).last().unwrap();
546        let expected = ewkb::Point::new(10.0, -20.0, Some(4326));
547        assert_eq!(point.x(), expected.x());
548        assert_eq!(point.y(), expected.y());
549        assert_eq!(point.srid, expected.srid);
550
551        let result = or_panic!(client.query("SELECT 'SRID=4326;POINT(10 -20 99)'::geometry", &[]));
552        let point = result.iter().map(|r| r.get::<_, ewkb::PointZ>(0)).last().unwrap();
553        assert_eq!(point, ewkb::PointZ { x: 10.0, y: -20.0, z: 99.0, srid: Some(4326) });
554
555        let result = or_panic!(client.query("SELECT 'POINT EMPTY'::geometry", &[]));
556        let point = result.iter().map(|r| r.get::<_, ewkb::Point>(0)).last().unwrap();
557        assert_eq!(&format!("{:?}", point), "Point { x: NaN, y: NaN, srid: None }");
558
559        let result = or_panic!(client.query("SELECT NULL::geometry(Point)", &[]));
560        let point = result.iter().map(|r| r.try_get::<_, ewkb::Point>(0)).last().unwrap();
561        assert_eq!(&format!("{:?}", point), "Err(Error { kind: FromSql(0), cause: Some(WasNull) })");
562    }
563
564	#[test]
565    #[ignore]
566    #[rustfmt::skip]
567    fn test_select_line() {
568        let mut client = connect();
569        let p = |x, y| ewkb::Point::new(x, y, None);
570        let result = or_panic!(client.query("SELECT ('LINESTRING (10 -20, -0 -0.5)')::geometry", &[]));
571        let line = result.iter().map(|r| r.get::<_, ewkb::LineString>(0)).last().unwrap();
572        assert_eq!(line, ewkb::LineString {srid: None, points: vec![p(10.0, -20.0), p(0., -0.5)]});
573
574        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
575        let result = or_panic!(client.query("SELECT ('SRID=4326;LINESTRING (10 -20, -0 -0.5)')::geometry", &[]));
576        let line = result.iter().map(|r| r.get::<_, ewkb::LineString>(0)).last().unwrap();
577        assert_eq!(line, ewkb::LineString {srid: Some(4326), points: vec![p(10.0, -20.0), p(0., -0.5)]});
578
579        let p = |x, y| ewkb::Point::new(x, y, Some(4326));
580        let result = or_panic!(client.query("SELECT ('SRID=4326;LINESTRINGZ (10 -20 1, -0 -0.5 1)')::geometry", &[]));
581        let line = result.iter().map(|r| r.get::<_, ewkb::LineString>(0)).last().unwrap();
582        assert_eq!(line, ewkb::LineString {srid: Some(4326), points: vec![p(10.0, -20.0), p(0., -0.5)]});
583
584        let result = or_panic!(client.query("SELECT 'LINESTRING EMPTY'::geometry", &[]));
585        let line = result.iter().map(|r| r.get::<_, ewkb::LineString>(0)).last().unwrap();
586        assert_eq!(&format!("{:?}", line), "LineStringT { points: [], srid: None }");
587    }
588
589	#[test]
590    #[ignore]
591    #[rustfmt::skip]
592    fn test_select_polygon() {
593        let mut client = connect();
594        let result = or_panic!(client.query("SELECT 'SRID=4326;POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry", &[]));
595        let poly = result.iter().map(|r| r.get::<_, ewkb::Polygon>(0)).last().unwrap();
596        assert_eq!(format!("{:.0?}", poly), "PolygonT { rings: [LineStringT { points: [Point { x: 0, y: 0, srid: Some(4326) }, Point { x: 2, y: 0, srid: Some(4326) }, Point { x: 2, y: 2, srid: Some(4326) }, Point { x: 0, y: 2, srid: Some(4326) }, Point { x: 0, y: 0, srid: Some(4326) }], srid: Some(4326) }], srid: Some(4326) }");
597    }
598
599	#[test]
600    #[ignore]
601    #[rustfmt::skip]
602    fn test_select_multipoint() {
603        let mut client = connect();
604        let result = or_panic!(client.query("SELECT 'SRID=4326;MULTIPOINT ((10 -20 100), (0 -0.5 101))'::geometry", &[]));
605        let points = result.iter().map(|r| r.get::<_, ewkb::MultiPointZ>(0)).last().unwrap();
606        assert_eq!(format!("{:.1?}", points), "MultiPointT { points: [PointZ { x: 10.0, y: -20.0, z: 100.0, srid: None }, PointZ { x: 0.0, y: -0.5, z: 101.0, srid: None }], srid: Some(4326) }");
607    }
608
609	#[test]
610    #[ignore]
611    #[rustfmt::skip]
612    fn test_select_multiline() {
613        let mut client = connect();
614        let result = or_panic!(client.query("SELECT 'SRID=4326;MULTILINESTRING ((10 -20, 0 -0.5), (0 0, 2 0))'::geometry", &[]));
615        let multiline = result.iter().map(|r| r.get::<_, ewkb::MultiLineString>(0)).last().unwrap();
616        assert_eq!(format!("{:.1?}", multiline), "MultiLineStringT { lines: [LineStringT { points: [Point { x: 10.0, y: -20.0, srid: None }, Point { x: 0.0, y: -0.5, srid: None }], srid: None }, LineStringT { points: [Point { x: 0.0, y: 0.0, srid: None }, Point { x: 2.0, y: 0.0, srid: None }], srid: None }], srid: Some(4326) }");
617    }
618
619	#[test]
620    #[ignore]
621    #[rustfmt::skip]
622    fn test_select_multipolygon() {
623        let mut client = connect();
624        let result = or_panic!(client.query("SELECT 'SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))'::geometry", &[]));
625        let multipoly = result.iter().map(|r| r.get::<_, ewkb::MultiPolygon>(0)).last().unwrap();
626        assert_eq!(format!("{:.0?}", multipoly), "MultiPolygonT { polygons: [PolygonT { rings: [LineStringT { points: [Point { x: 0, y: 0, srid: None }, Point { x: 2, y: 0, srid: None }, Point { x: 2, y: 2, srid: None }, Point { x: 0, y: 2, srid: None }, Point { x: 0, y: 0, srid: None }], srid: None }], srid: None }, PolygonT { rings: [LineStringT { points: [Point { x: 10, y: 10, srid: None }, Point { x: -2, y: 10, srid: None }, Point { x: -2, y: -2, srid: None }, Point { x: 10, y: -2, srid: None }, Point { x: 10, y: 10, srid: None }], srid: None }], srid: None }], srid: Some(4326) }");
627    }
628
629	#[test]
630    #[ignore]
631    #[rustfmt::skip]
632    fn test_select_geometrycollection() {
633        let mut client = connect();
634        let result = or_panic!(client.query("SELECT 'GeometryCollection(POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20))'::geometry", &[]));
635        let geom = result.iter().map(|r| r.get::<_, ewkb::GeometryCollection>(0)).last().unwrap();
636        assert_eq!(format!("{:.0?}", geom), "GeometryCollectionT { geometries: [Point(Point { x: 10, y: 10, srid: None }), Point(Point { x: 30, y: 30, srid: None }), LineString(LineStringT { points: [Point { x: 15, y: 15, srid: None }, Point { x: 20, y: 20, srid: None }], srid: None })], srid: None }");
637    }
638
639	#[test]
640    #[ignore]
641    #[rustfmt::skip]
642    fn test_select_geometry() {
643        let mut client = connect();
644        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry)", &[]));
645        or_panic!(client.execute("INSERT INTO geomtests VALUES('SRID=4326;POINT(10 -20 99)'::geometry)", &[]));
646        let result = or_panic!(client.query("SELECT geom FROM geomtests", &[]));
647        let geom = result.iter().map(|r| r.get::<_, ewkb::GeometryZ>(0)).last().unwrap();
648        assert_eq!(format!("{:.0?}", geom), "Point(PointZ { x: 10, y: -20, z: 99, srid: Some(4326) })");
649    }
650
651	#[test]
652    #[ignore]
653    #[rustfmt::skip]
654    fn test_select_type_error() {
655        let mut client = connect();
656        let result = or_panic!(client.query("SELECT ('LINESTRING (10 -20, -0 -0.5)')::geometry", &[]));
657        let poly = result.iter().map(|r| r.try_get::<_, ewkb::Polygon>(0)).last().unwrap();
658        assert_eq!(format!("{:?}", poly), "Err(Error { kind: FromSql(0), cause: Some(\"cannot convert geometry to PolygonT\") })");
659    }
660
661	#[test]
662    #[ignore]
663    #[rustfmt::skip]
664    fn test_twkb() {
665        let mut client = connect();
666        let result = or_panic!(client.query("SELECT ST_AsTWKB('POINT(10 -20)'::geometry)", &[]));
667        let point = result.iter().map(|r| r.get::<_, twkb::Point>(0)).last().unwrap();
668        assert_eq!(point, twkb::Point {x: 10.0, y: -20.0});
669
670        let result = or_panic!(client.query("SELECT ST_AsTWKB('SRID=4326;POINT(10 -20)'::geometry)", &[]));
671        let point = result.iter().map(|r| r.get::<_, twkb::Point>(0)).last().unwrap();
672        assert_eq!(point, twkb::Point {x: 10.0, y: -20.0});
673
674        let result = or_panic!(client.query("SELECT ST_AsTWKB('POINT EMPTY'::geometry)", &[]));
675        let point = result.iter().map(|r| r.get::<_, twkb::Point>(0)).last().unwrap();
676        assert_eq!(&format!("{:?}", point), "Point { x: NaN, y: NaN }");
677        let point = &point as &dyn postgis::Point;
678        assert!(point.x().is_nan());
679
680        let result = or_panic!(client.query("SELECT ST_AsTWKB(NULL::geometry(Point))", &[]));
681        let point = result.iter().map(|r| r.try_get::<_, twkb::Point>(0)).last().unwrap();
682        assert_eq!(&format!("{:?}", point), "Err(Error { kind: FromSql(0), cause: Some(WasNull) })");
683
684        let result = or_panic!(client.query("SELECT ST_AsTWKB('LINESTRING (10 -20, -0 -0.5)'::geometry, 1)", &[]));
685        let line = result.iter().map(|r| r.get::<_, twkb::LineString>(0)).last().unwrap();
686        assert_eq!(&format!("{:.1?}", line), "LineString { points: [Point { x: 10.0, y: -20.0 }, Point { x: 0.0, y: -0.5 }] }");
687    }
688
689	#[test]
690    #[ignore]
691    #[rustfmt::skip]
692    fn test_twkb_insert() {
693        let mut client = connect();
694        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(Point))", &[]));
695
696        let result = or_panic!(client.query("SELECT ST_AsTWKB('POINT(10 -20)'::geometry)", &[]));
697        let point = result.iter().map(|r| r.get::<_, twkb::Point>(0)).last().unwrap();
698
699        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&point.as_ewkb()]));
700        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('POINT(10 -20)') FROM geomtests", &[]));
701        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
702        or_panic!(client.execute("TRUNCATE geomtests", &[]));
703
704        let mut client = connect();
705        or_panic!(client.execute("CREATE TEMPORARY TABLE geomtests (geom geometry(LineString))", &[]));
706
707        let result = or_panic!(client.query("SELECT ST_AsTWKB('LINESTRING (10 -20, 0 -0.5)'::geometry, 1)", &[]));
708        let line = result.iter().map(|r| r.get::<_, twkb::LineString>(0)).last().unwrap();
709
710        or_panic!(client.execute("INSERT INTO geomtests (geom) VALUES ($1)", &[&line.as_ewkb()]));
711        let result = or_panic!(client.query("SELECT geom=ST_GeomFromEWKT('LINESTRING (10 -20, 0 -0.5)') FROM geomtests", &[]));
712        assert!(result.iter().map(|r| r.get::<_, bool>(0)).last().unwrap());
713        or_panic!(client.execute("TRUNCATE geomtests", &[]));
714    }
715
716	#[test]
717    #[ignore]
718    #[rustfmt::skip]
719    #[allow(unused_imports,unused_variables)]
720    fn test_examples() {
721        use postgres::{Client, NoTls};
722        //use postgis::ewkb;
723        //use postgis::LineString;
724
725        fn main() {
726            //
727            use crate::{
728                ewkb,
729                types::LineString,
730                twkb
731            };
732            let mut client = connect();
733            or_panic!(client.execute("CREATE TEMPORARY TABLE busline (route geometry(LineString))", &[]));
734            or_panic!(client.execute("CREATE TEMPORARY TABLE stops (stop geometry(Point))", &[]));
735            or_panic!(client.execute("INSERT INTO busline (route) VALUES ('LINESTRING(10 -20, -0 -0.5)'::geometry)", &[]));
736            //
737
738            // conn ....
739            for row in &client.query("SELECT * FROM busline", &[]).unwrap() {
740                let route: ewkb::LineString = row.get("route");
741                let last_stop = route.points().last().unwrap();
742                let _ = client.execute("INSERT INTO stops (stop) VALUES ($1)", &[&last_stop]);
743            }
744
745            for row in &client.query("SELECT * FROM busline", &[]).unwrap() {
746                let route = row.try_get::<_, Option<ewkb::LineString>>("route");
747                match route {
748                    Ok(Some(geom)) => { println!("{:?}", geom) }
749                    Ok(None) => { /* Handle NULL value */ }
750                    Err(err) => { println!("Error: {}", err) }
751                }
752            }
753
754        //use postgis::twkb;
755
756            for row in &client.query("SELECT ST_AsTWKB(route) FROM busline", &[]).unwrap() {
757                let route: twkb::LineString = row.get(0);
758                let last_stop = route.points().last().unwrap();
759                let _ = client.execute("INSERT INTO stops (stop) VALUES ($1)", &[&last_stop.as_ewkb()]);
760            }
761        }
762
763        main();
764    }
765}