Closed sid6mathur closed 9 months ago
That's an interesting use case. Thanks for opening the issue.
For Scany to know how to handle a custom type, the type needs to implement the sql.Scanner
interface. The problem with orb.Point{}
is that it doesn't implement it on its own, only after being manually wrapped via the ewkb.Scanner()
function call. To solve this, you should implement a custom wrapper type around orb.Point{}
using ewkb.Scanner()
internally, something like ScannablePoint{}
, which has the Scan()
method and conforms to the sql.Scanner
interface.
I hope this helps. Let me know if you more help with this.
Thanks for your tips, @georgysavva . I realized while attempting a composition of the nature you suggested that the GeometryScanner
type in orb
's encoding/ewkb
already supports the sql.Scanner
interface, and hence works out of the box with scany
using pgx
as follows.
Postgresql+PostGIS table configured as follows:
CREATE EXTENSION IF NOT EXISTS postgis;
DROP TABLE IF EXISTS test_regions;
CREATE TABLE test_regions (
regionid TEXT, -- 'us-east2' OR 'usgovcentral' OR 'china-east2'
geom geometry(Point, 4326) -- a PostGIS geolocation with SRID configured as 4326 aka GPS
);
INSERT INTO test_regions (regionid, geom) VALUES
('us-west-2', ST_SetSRID(ST_GeomFromText('POINT(-122.68 45.52)'), 4326)),
('ap-southeast-2', ST_SetSRID(ST_GeomFromText('POINT(151.21 -33.87)'), 4326));
The above table with two rows can be read successfully using scany
as follows:
package main
import (
"context"
"fmt"
"os"
"github.com/georgysavva/scany/v2/pgxscan"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/paulmach/orb/encoding/ewkb"
)
// Represents (say) a datacenter region in a cloud company's infrastructure and its geolocation
type Region struct {
RegionId string `db:"regionid"`
Loc ewkb.GeometryScanner `db:"geom"` // The scanner type wraps an orb.Geometry
}
func main() {
var err error
db, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
panic(err)
}
var regions []*Region
// Note of the use of PostGIS ST_AsEWKB() in the query below
rows, err := db.Query(context.Background(), `SELECT regionid, ST_AsEWKB(geom) as geom FROM test_regions;`)
if err != nil {
panic(err)
}
defer rows.Close()
if err := pgxscan.ScanAll(®ions, rows); err != nil {
panic(err)
}
for _, r := range regions {
if !r.Loc.Valid {
fmt.Printf("Warning: Invalid geometry for %s\n", r.RegionId)
continue
}
fmt.Printf("Region = %s at PostGIS geometry [type = %s, projection/SRID = %d, lng/lat = %v]\n",
r.RegionId,
r.Loc.Geometry.GeoJSONType(), r.Loc.SRID, r.Loc.Geometry)
}
}
Note that the above code uses orb
's ewkb.GeometryScanner
which in turn uses the generic type orb.Geometry
.
The two data center locations are printed correctly:
Region = us-west-2 at PostGIS geometry [type = Point, projection/SRID = 4326, lng/lat = [-122.68 45.52]]
Region = ap-southeast-2 at PostGIS geometry [type = Point, projection/SRID = 4326, lng/lat = [151.21 -33.87]]
At your discretion, you may add the above PostGIS interop via https://github.com/paulmach/orb and its ewkb.GeometryScanner
to your README, and then close this issue.
Thanks very much!
I am glad you were able to solve your issue. I don't think adding this information to Scany's Readme is necessary because your use case is pretty niche, and other users don't seem to have similar problems. But thank you for your investigation. If more use cases come up in the future, I will document this somewhere.
Any tips on how to scan Postgis types into structs with
scany
while still maintaining the high-level abstractions that it helps with ?A sample table has a PostGIS geometry type amongst many vanilla types:
The Go struct type to scan into:
Now, the above code is missing the logic for parsing the PostGIS
geometry
type into anorb.Point
(Fororb.Point
, see https://github.com/paulmach/orb)Now, the wonderful author of
orb.Point
does provide a utility function that implements theScanner
interface fromdatabase/sql
of stlib. https://pkg.go.dev/github.com/paulmach/orb/encoding/ewkbTheir example for using the
ewkb.Scanner
wrapper to slurp PostGIS data into anorb.Point
is in the call toScan()
below:Given that
scany
doesn't have aScan
function that can takeScanner
implementing types, I am confused on how to useewkb.Scanner
to swizzle a PostGIS point into anorb.Point
type struct member , while still usingscany
for all my other struct members.Any guidance appreciated :)