sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

Support geometric types for postgres

Open mehcode opened this issue 4 years ago • 19 comments

https://docs.rs/geo https://www.postgresql.org/docs/current/datatype-geometric.html

  • POINT -> geo::Coordinate<f64>
  • LINE -> geo::Line<f64>
  • LSEG -> geo::Line<f64>
  • BOX -> geo::Rect<f64>
  • PATH -> geo::LineString<f64>
  • POLYGON -> geo::Polygon<f64>

mehcode avatar Mar 26 '20 08:03 mehcode

All types

OID sources: https://github.com/postgres/postgres/blob/2743d9ae4a490a7d96b5c19d50694bd101a87dc8/src/include/catalog/pg_type.dat

Text encoding descriptions: https://www.postgresql.org/docs/current/datatype-geometric.html The documentation specifies which text syntax you're looking for. For robustness, the implementation should not assume any specifics about whitespace around characters.

For text encoding, notice that several types share similar formats, so you may not need exactly one function per type.

Binary encoding implementation in C: https://github.com/postgres/postgres/blob/2743d9ae4a490a7d96b5c19d50694bd101a87dc8/src/backend/utils/adt/geo_ops.c

All values are big (network)-endian encoded.

POINT

  • type OID 600, array type OID 1017 pg_type.dat lines 183-187
  • Binary encoding: geo_ops.c line 1831
f64: x
f64: y
  • Text encoding:
( x , y )

LINE

*type OID 628, array type OID 629 pg_type.dat lines 209-212

  • Binary encoding: geo_ops.c line 1034
f64: A
f64: B
f64: C

where A, B, and C are coefficients in the equation Ax + By + C = 0

  • Text encoding: { A, B, C }

LSEG

  • type OID 601, array type OID 1018 pg_type.dat lines 188-192
  • Binary encoding: geo_ops.c line 2064
f64: x1
f64: y1
f64: x2
f64: y2
  • Text encoding:
[ ( x1 , y1 ) , ( x2 , y2 ) ]

BOX

  • type OID 603, array type OID 1020 pg_type.dat lines 198-203
  • Binary encoding: geo_ops.c line 489
f64: x1
f64: y1
f64: x2,
f64: y2

where (x1, y1) is the top-left corner and (x2, y2) is the bottom-right corner.

Text encoding:

( x1 , y1 ) , ( x2 , y2 )

PATH

  • type OID 602, array type OID 1019 pg_type.dat lines 193-197

  • Binary encoding: geo_ops.c line 1485

i8: closed (0 for false or 1 for true)
i32: N, number of points
for _ in 0 .. N {
    f64: point.x
    f64: point.y
}
  • Text encoding:
[ ( x1, y1) , ... , ( xN , yN ) ] : open form
( ( x1 , y1 ) , ... , ( xN , yN ) ) : closed form

POLYGON

  • type OID 604, array type OID 1027 pg_types.dat lines 204-208

  • Binary encoding: geo_ops.c line 3556

i32: N, number of points
for _ in 0 .. N {
    f64: point.x
    f64: point.y
}
  • Text encoding:
( ( x1 , y1 ) , ... , ( xN , yN ) )

abonander avatar Mar 30 '20 01:03 abonander

* `POLYGON` -> `geo::Polygon<f64>`

geo::Polygon is not equivalent to POLYGON as it also handles holes within the bounded area.

Freax13 avatar Feb 02 '21 16:02 Freax13

I just left a comment on the geo crate (see mention above) and I'm also aware of https://github.com/pka/geozero which implemented the WKT/WKB types already. As I'm interested in working on this feature for the purpose of getting 3D points for my application, I'm curious what acceptable approach is.

I would frankly find it irritating to need two separate features for 2D and 3D points, but on the other hand I'm not sure if it can be avoided, since PostgreSQL itself implements a 2D point, while something like PostGIS implements others. So perhaps it's just a question of feature naming, "geo" implies the geo crate, while maybe geozero give you the "postgis" features?

I'd be personally tempted to start with the postgis feature, unless anyone can see a way in which these are dependent on each other?

nixpulvis avatar Feb 25 '21 00:02 nixpulvis

In the geospatial world, geometries are almost exclusively used together with the PostGIS extension. So implementing the PostgreSQL geometric types has not very high priority. As mentioned, geozero supports encoding/decoding PostGIS geometries with SQLx (example).

pka avatar Feb 25 '21 07:02 pka

OK, so it sounds like my first task should be to extend geozero with PointZ support for sqlx. It seems like this shouldn't require any changes here, since the conversions are done in that crate, and it's already working for other geometries.

Given that you need to enable the extension postgis for this in PostgreSQL, I would love to have everything you need from geozero inside a postgis module somewhere, even if they're just aliases. This would help new users to find the correct types in the somewhat complex geometry situation.

As for native PostgreSQL geometries, I can hopefully come back to this after I get a solid grasp on the postgis types.

nixpulvis avatar Feb 25 '21 15:02 nixpulvis

@pka, IDK maybe I'm just missing something, but I cannot get the example to work in my application, and all the tests for postgis are marked as [ignore]. I'm getting the error error: unsupported type geometry of column #9 ("<column name>").

nixpulvis avatar Feb 25 '21 19:02 nixpulvis

@nixpulvis The PostGIS tests were broken indeed. Here are the setup instructions: https://github.com/georust/geozero/blob/master/geozero/tests/data/postgis.sql#L1. Please report any failures in the geozero repo.

pka avatar Feb 25 '21 21:02 pka

I'm not sure with this given example how to make sqlx and geozero play nicely with a struct, though? Having a field with wkb::Decode doesn't seem to be enough there, and it's not clear how to involve a type override.

bbqsrc avatar Mar 12 '21 13:03 bbqsrc

@bbqsrc if you want to read a geometry as part of a struct, you have to use wkb::Decode<YourGeomType> as field type (Example), or you implement sqlx::decode::Decode for your geometry type (see also https://docs.rs/geozero/0.7.4/geozero/index.html#macros).

pka avatar Mar 12 '21 14:03 pka

I'm not sure why this issue drifted to geospatial support when the OP clearly meant only geometric support? Also, did you note the pull request by @qtbeee for support of geometric types? In fact, @abonander actually mentioned that this was nothing to do with geospatial support in the conversation on that pull request.

I'm aware that @pka said:

In the geospatial world, geometries are almost exclusively used together with the PostGIS extension. So implementing the PostgreSQL geometric types has not very high priority. As mentioned, geozero supports encoding/decoding PostGIS geometries with SQLx (example).

But note that he (correctly) mentioned that this was the case in what he calls the geospatial world. There are use cases (such as image and document analysis) which require geometric support that have nothing to do with GIS. I therefore respectfully disagree with the implementation of geometric types having a low priority simply because they're less important to geoinformatics.

I suggest starting a new issue for geospatial support and making this ticket about Postgres Geometry types as the OP intended.

Gearme avatar Mar 14 '21 15:03 Gearme

I'm not sure why this issue drifted to geospatial support when the OP clearly meant only geometric support?

I'm sorry for causing some drifting here. My main issue was that I needed 3D points, which unfortunately aren't supported without extensions. I wouldn't say that 3D points are fundamentally any more geospatial than geometrical, you just happen to need PostGIS to get 3D points.

I wasn't sure if there was some desire to unify the types between "native" and PostGIS primitives.

nixpulvis avatar Mar 14 '21 17:03 nixpulvis

Are there any workarounds for just inserting a point into a new row? Should I just use unchecked?

camsjams avatar Jul 07 '21 18:07 camsjams

Given how often we've run into semver issues with using external crates for data modeling, I think I'd rather just provide bespoke structs for mapping these types.

abonander avatar Jul 11 '22 20:07 abonander

Just for reference, I could not get the query! macro to work with inserting geometry types in PostGIS by following the linked examples. As @camsjams alludes, everything works fine using the query function. I was seeing the same error as @nixpulvis above when using the macro.

I spent a lot of time trying to get the macro to work fruitlessly, while the function worked fine. Not complaining! Sqlx is great! Thank you so much! Just pointing out for others running into the same issue, since it wasn't explicitly pointed out here.

stevemarin avatar Sep 07 '22 02:09 stevemarin

Hi, I would very much like to contribute geometry type support (and postgis type support, if wanted) - but I am a bit confused about the current state of affairs?

Is postgis support natively in sqlx wanted? (In my opinion it would be a good thing, it is a semi-popular standard and hidden behind a feature gate it wouldn't incure any cost for people not using postgis)

This especially matters for me since as far as I see it, it is not possible to add third party types to sqlx which are checked at compile time

So - if support for postgis is wanted I would be happy to contribute, but should we add custom types to sqlx itself or use a different rust library for that? Intuitivly I would choose a different, more established library, but @abonander argued against it and imo best suited library geo-types didn't hit 1.0 yet (even though the release cycle doesn't seem to be too fast)

And should the WKB decoding be bespoke? There is https://docs.rs/wkb/latest/wkb/, but this would lock us into geo-types and would be another potential semver issue. And postgis uses EWKB anyway, which is not difficult to implement, but I don't want to impose an unwanted maintenance burden on anyone

I would love feedback and I would also feel comfortable writing a draft PR for support, but only if this is actually a wanted feature, apology if I oversaw any prior discussion.

VincentWo avatar Oct 12 '22 10:10 VincentWo

What is the current consensus, do we use the geo-types or roll our own?

As a compromise I would propose we add public traits to allow custom types for the deserialization. Throug New Types you could use these traits to add suport for geo-types for example.

Or we make traits with assoziate types, so you can create a ZST implementing these to simulate implementing them for external types.

PS: if we roll our own type do we want a feature flag for them?

mio991 avatar Feb 24 '24 08:02 mio991

same question here. What is still missing/required to proceed? And it seems also blocking the postgis support from the SeaORM side if my understanding is correct?

tumluliu avatar Mar 31 '24 09:03 tumluliu

There should be sufficient traits to implement decoding for any given type. You'd just have to request it using the type override syntax (see the docs), making the macros choose an external type by default is a different question entirely.

The biggest issue with integrating an external crate is stability. Is it yet another crate we're going to have to upgrade with every minor (0.x.y -> 0.{x+1}.0 release? That's just something we have to consider due to its effects on the burden of maintenance.

abonander avatar Mar 31 '24 09:03 abonander

Any updates?

LockedThread avatar Apr 10 '24 10:04 LockedThread