diff --git a/provider/postgis/error.go b/provider/postgis/error.go index 9b3180669..02884522c 100644 --- a/provider/postgis/error.go +++ b/provider/postgis/error.go @@ -21,3 +21,12 @@ type ErrUnclosedToken string func (e ErrUnclosedToken) Error() string { return fmt.Sprintf("postgis: unclosed token in (%v)", string(e)) } + +type ErrGeomFieldNotFound struct { + GeomFieldName string + LayerName string +} + +func (e ErrGeomFieldNotFound) Error() string { + return fmt.Sprintf("postgis: geom fieldname (%v) not found for layer (%v)", e.GeomFieldName, e.LayerName) +} diff --git a/provider/postgis/postgis.go b/provider/postgis/postgis.go index 81a3ccf3b..ee3590fc1 100644 --- a/provider/postgis/postgis.go +++ b/provider/postgis/postgis.go @@ -539,6 +539,21 @@ func (p Provider) TileFeatures(ctx context.Context, layer string, tile provider. // fetch rows FieldDescriptions. this gives us the OID for the data types returned to aid in decoding fdescs := rows.FieldDescriptions() + // loop our field descriptions looking for the geometry field + var geomFieldFound bool + for i := range fdescs { + if fdescs[i].Name == plyr.GeomFieldName() { + geomFieldFound = true + break + } + } + if !geomFieldFound { + return ErrGeomFieldNotFound{ + GeomFieldName: plyr.GeomFieldName(), + LayerName: plyr.Name(), + } + } + for rows.Next() { // context check if err := ctx.Err(); err != nil { diff --git a/provider/postgis/postgis_test.go b/provider/postgis/postgis_test.go index 5c26faa3e..1694cd2ab 100644 --- a/provider/postgis/postgis_test.go +++ b/provider/postgis/postgis_test.go @@ -225,6 +225,7 @@ func TestTileFeatures(t *testing.T) { type tcase struct { layerConfig map[string]interface{} tile *slippy.Tile + expectedErr error expectedFeatureCount int expectedTags []string } @@ -268,8 +269,8 @@ func TestTileFeatures(t *testing.T) { return nil }) - if err != nil { - t.Errorf("unexpected err: %v", err) + if err != tc.expectedErr { + t.Errorf("expected err (%v) got err (%v)", tc.expectedErr, err) return } @@ -439,12 +440,24 @@ func TestTileFeatures(t *testing.T) { postgis.ConfigKeyGeomIDField: "id", postgis.ConfigKeyGeomField: "geometry", // this SQL is a workaround the normal !BBOX! WHERE clause. we're simulating a null geometry lookup in the table and don't want to filter by bounding box - postgis.ConfigKeySQL: "SELECT id, ST_AsBinary(geometry) AS geometry, !BBOX! as bbox FROM null_geom_test", + postgis.ConfigKeySQL: "SELECT id, ST_AsBinary(geometry) AS geometry, !BBOX! AS bbox FROM null_geom_test", }, tile: slippy.NewTile(16, 11241, 26168, 64, tegola.WebMercator), expectedFeatureCount: 1, expectedTags: []string{"bbox"}, }, + "missing geom field name": { + layerConfig: map[string]interface{}{ + postgis.ConfigKeyLayerName: "missing_geom_field_name", + postgis.ConfigKeyGeomField: "geom", + postgis.ConfigKeySQL: "SELECT ST_AsBinary(geom) FROM three_d_test WHERE geom && !BBOX!", + }, + tile: slippy.NewTile(16, 11241, 26168, 64, tegola.WebMercator), + expectedErr: postgis.ErrGeomFieldNotFound{ + GeomFieldName: "geom", + LayerName: "missing_geom_field_name", + }, + }, } for name, tc := range tests { diff --git a/provider/postgis/util.go b/provider/postgis/util.go index 47881908c..73d2d47f4 100644 --- a/provider/postgis/util.go +++ b/provider/postgis/util.go @@ -189,8 +189,10 @@ func transformVal(valType pgtype.OID, val interface{}) (interface{}, error) { } } -func decipherFields(ctx context.Context, geoFieldname, idFieldname string, descriptions []pgx.FieldDescription, values []interface{}) (gid uint64, geom []byte, tags map[string]interface{}, err error) { +// decipherFields is responsible for processing the SQL result set, decoding geometries, ids and feature tags. +func decipherFields(ctx context.Context, geomFieldname, idFieldname string, descriptions []pgx.FieldDescription, values []interface{}) (gid uint64, geom []byte, tags map[string]interface{}, err error) { var ok bool + tags = make(map[string]interface{}) for i := range values { @@ -207,9 +209,9 @@ func decipherFields(ctx context.Context, geoFieldname, idFieldname string, descr desc := descriptions[i] switch desc.Name { - case geoFieldname: + case geomFieldname: if geom, ok = values[i].([]byte); !ok { - return 0, nil, nil, fmt.Errorf("unable to convert geometry field (%v) into bytes.", geoFieldname) + return 0, nil, nil, fmt.Errorf("unable to convert geometry field (%v) into bytes.", geomFieldname) } case idFieldname: gid, err = gId(values[i]) @@ -238,7 +240,7 @@ func decipherFields(ctx context.Context, geoFieldname, idFieldname string, descr } } - return gid, geom, tags, err + return gid, geom, tags, nil } func gId(v interface{}) (gid uint64, err error) {