Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
46407: kvcoord: small fixes r=andreimatei a=andreimatei

See individual commits.

52740: sql: owning a schema gives privilege to drop tables in schema r=RichardJCai a=RichardJCai

sql: owning a schema gives privilege to drop tables in schema

Release note (sql change): Schema owners can drop tables inside
the schema without explicit DROP privilege on the table.

Fixes #51931

53127: builtins: use ST_Union as aggregate r=rytaft,sumeerbhola a=otan

Add the ST_Union aggregate, removing the two-argument version
temporarily as we cannot currently have an aggregate and non-aggregate
at the same time. This is ok since we haven't released yet, and from
reading it seems more likely people will use the aggregate version.

Release note (sql change): Implement the ST_Union builtin as an
aggregate. The previous alpha-available ST_Union for two arguments is
deprecated.

53162: builtins: implement ST_GeomFromGeoHash/ST_Box2DFromGeoHash r=sumeerbhola a=otan

Resolves #48806

Release note (sql change): Implemented the ST_GeomFromGeoHash and
ST_Box2DFromGeoHash builtins.

53181: sql/kv: avoid expensive heap allocations with inverted joins r=nvanbenschoten a=nvanbenschoten

This PR contains three optimizations that reduce heap allocations by
inverted join operations. These were found by profiling the following
geospatial query:
```sql
SELECT Count(census.blkid), Count(subways.name)
FROM nyc_census_blocks AS census
JOIN nyc_subway_stations AS subways
ON ST_Intersects(subways.geom, census.geom);
```

Combined, these three commits speed up the query by a little over **3%**.

```
name                                old ms    new ms    delta
Test/postgis_geometry_tutorial/nyc  139 ±12%  135 ±11%  -3.04%  (p=0.000 n=241+243)
```

#### sql/rowexec: avoid heap allocations in batchedInvertedExprEvaluator

This commit restructures the slice manipulation performed in
`(*batchedInvertedExprEvaluator).init` to avoid heap allocations. Before
the change, this method used to contain the first and fifth most
expensive memory allocations by total allocation size (`alloc_space`)
in a geospatial query of interest:
```
----------------------------------------------------------+-------------
                                         3994.06MB   100% |   github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*invertedJoiner).readInput /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_joiner.go:418
 3994.06MB 10.70% 10.70%  3994.06MB 10.70%                | github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*batchedInvertedExprEvaluator).init /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_expr_evaluator.go:366
----------------------------------------------------------+-------------

----------------------------------------------------------+-------------
                                          954.07MB   100% |   github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*invertedJoiner).readInput /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_joiner.go:418
  954.07MB  4.61% 41.71%   954.07MB  4.61%                | github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*batchedInvertedExprEvaluator).init /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_expr_evaluator.go:409
----------------------------------------------------------+-------------
```

The largest offender here was the `routingSpans` slice, which made no
attempt to recycle memory. The other offender was the `pendingSpans`
slice. We made an attempt to recycle the memory in this slice, except
we truncated it from the front instead of the back, so we ended us
not actually recycling anything.

This commit addresses this in two ways. First, it updates the `pendingSpans`
slice to point into the `routingSpans` slice so the two can share memory.
`pendingSpans` becomes a mutable window into `routingSpans`, so it no
longer requires its own heap allocation. Next, the commit updates the
memory recycling to recycle `routingSpans` instead of `pendingSpans`.
Since we never truncate `routingSpans` from the front, the recycling
now works.

With these two changes, the two heap allocations, which used to account
for **15.31%** of total allocated space combined drops to a single heap
allocation that accounts for only **2.43%** of total allocated space on
the geospatial query.

```
----------------------------------------------------------+-------------
                                          182.62MB   100% |   github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*invertedJoiner).readInput /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_joiner.go:418
  182.62MB  2.43% 58.36%   182.62MB  2.43%                | github.com/cockroachdb/cockroach/pkg/sql/rowexec.(*batchedInvertedExprEvaluator).init /go/src/github.com/cockroachdb/cockroach/pkg/sql/rowexec/inverted_expr_evaluator.go:366
----------------------------------------------------------+-------------
```

#### sql: properly copy span slices

This commit fixes two locations where we not optimally performing copies
from one slice to another.

In `invertedJoiner.generateSpans`, we were not pre-allocating the
destination slice and were using append instead of direct assignment,
which is measurably slower due to the extra writes to the slice header.

In `getProtoSpans`, we again were appending to a slice with zero length
and sufficient capacity instead of assigning to a slice with sufficient
length.

#### kv: check for expensive logging before VEventf in appendRefreshSpans

Before this change, we called VEventf directly with the refresh span
corresponding to each request in a batch. This caused each span object
to allocate while passing through an `interface{}`. This could be seen
in heap profiles, where it accounted for **1.89%** of total allocated
memory (`alloc_space`) in a geospatial query of interest:

```
----------------------------------------------------------+-------------
                                          142.01MB   100% |   github.com/cockroachdb/cockroach/pkg/roachpb.(*BatchRequest).RefreshSpanIterate /go/src/github.com/cockroachdb/cockroach/pkg/roachpb/batch.go:407
  142.01MB  1.89% 68.71%   142.01MB  1.89%                | github.com/cockroachdb/cockroach/pkg/kv/kvclient/kvcoord.(*txnSpanRefresher).appendRefreshSpans.func1 /go/src/github.com/cockroachdb/cockroach/pkg/kv/kvclient/kvcoord/txn_interceptor_span_refresher.go:510
----------------------------------------------------------+-------------
```

This commit adds a guard around the VEventf to avoid the allocation.

53196: sqlliveness/slstorage,timeutil: various improvements r=spaskob a=ajwerner

See individual commits. This PR addresses some anxieties I had about the implementation of slstorage. In particular, it makes the testing deterministic by giving it a new table independent of the host cluster's state and allows injecting time. It then changes the GC to be less frequent and instead relies on failing out sessions when it determines they are expired. This is important to avoid spinning between when a session is deemed expired and when it would later be deleted by a gc interval. It also jitters that GC interval to avoid problems due to contention in larger clusters. 

53214: serverpb: add docstrings to `HotRangesRequest` and `HotRangesResponse` r=mjibson a=knz

This is part of a project to document the public HTTP APIs.

@mjibson you'll notice in this PR that the doc generator doesn't deal well with newline characters in docstrings.

Co-authored-by: Andrei Matei <andrei@cockroachlabs.com>
Co-authored-by: richardjcai <caioftherichard@gmail.com>
Co-authored-by: Oliver Tan <otan@cockroachlabs.com>
Co-authored-by: Nathan VanBenschoten <nvanbenschoten@gmail.com>
Co-authored-by: Andrew Werner <ajwerner@cockroachlabs.com>
Co-authored-by: Raphael 'kena' Poss <knz@thaumogen.net>
  • Loading branch information
7 people committed Aug 21, 2020
8 parents 86165b9 + 8b801ad + 0bef16c + b3d6f2e + c022554 + e69f860 + f0f85e5 + 05b0b89 commit f55266f
Show file tree
Hide file tree
Showing 47 changed files with 1,515 additions and 891 deletions.
24 changes: 15 additions & 9 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -1431,7 +1431,9 @@ information about the resources on a node used by that table.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | If left empty, hot ranges for all nodes/stores will be returned. |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. |



Expand All @@ -1446,8 +1448,8 @@ information about the resources on a node used by that table.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | NodeID is the node that submitted all the requests. |
| hot_ranges_by_node_id | [HotRangesResponse.HotRangesByNodeIdEntry](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRangesByNodeIdEntry) | repeated | |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | NodeID is the node that received the HotRangesRequest and forwarded requests to the selected target node(s). |
| hot_ranges_by_node_id | [HotRangesResponse.HotRangesByNodeIdEntry](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRangesByNodeIdEntry) | repeated | HotRangesByNodeID contains a hot range report for each selected target node ID in the HotRangesRequest. |



Expand All @@ -1471,8 +1473,10 @@ information about the resources on a node used by that table.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error_message | [string](#cockroach.server.serverpb.HotRangesResponse-string) | | |
| stores | [HotRangesResponse.StoreResponse](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.StoreResponse) | repeated | |
| error_message | [string](#cockroach.server.serverpb.HotRangesResponse-string) | | ErrorMessage is set to a non-empty string if this target node was unable to produce a hot range report.

The contents of this string indicates the cause of the failure. |
| stores | [HotRangesResponse.StoreResponse](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.StoreResponse) | repeated | Stores contains the hot ranges report if no error was encountered. There is one part to the report for each store in the target node. |



Expand All @@ -1483,8 +1487,8 @@ information about the resources on a node used by that table.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| store_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | |
| hot_ranges | [HotRangesResponse.HotRange](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRange) | repeated | |
| store_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | StoreID identifies the store for which the report was produced. |
| hot_ranges | [HotRangesResponse.HotRange](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRange) | repeated | HotRanges is the hot ranges report for this store on the target node. |



Expand All @@ -1495,8 +1499,10 @@ information about the resources on a node used by that table.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.server.serverpb.HotRangesResponse-cockroach.roachpb.RangeDescriptor) | | |
| queries_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.server.serverpb.HotRangesResponse-cockroach.roachpb.RangeDescriptor) | | Desc is the descriptor of the range for which the report was produced.

Note: this field is generally RESERVED and will likely be removed or replaced in a later version. See: https://github.com/cockroachdb/cockroach/issues/53212 |
| queries_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | QueriesPerSecond is the recent number of queries per second on this range. |



Expand Down
24 changes: 15 additions & 9 deletions docs/generated/http/hotranges.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | If left empty, hot ranges for all nodes/stores will be returned. |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. |



Expand All @@ -22,8 +24,8 @@

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | NodeID is the node that submitted all the requests. |
| hot_ranges_by_node_id | [HotRangesResponse.HotRangesByNodeIdEntry](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRangesByNodeIdEntry) | repeated | |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | NodeID is the node that received the HotRangesRequest and forwarded requests to the selected target node(s). |
| hot_ranges_by_node_id | [HotRangesResponse.HotRangesByNodeIdEntry](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRangesByNodeIdEntry) | repeated | HotRangesByNodeID contains a hot range report for each selected target node ID in the HotRangesRequest. |



Expand All @@ -47,8 +49,10 @@

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error_message | [string](#cockroach.server.serverpb.HotRangesResponse-string) | | |
| stores | [HotRangesResponse.StoreResponse](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.StoreResponse) | repeated | |
| error_message | [string](#cockroach.server.serverpb.HotRangesResponse-string) | | ErrorMessage is set to a non-empty string if this target node was unable to produce a hot range report.

The contents of this string indicates the cause of the failure. |
| stores | [HotRangesResponse.StoreResponse](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.StoreResponse) | repeated | Stores contains the hot ranges report if no error was encountered. There is one part to the report for each store in the target node. |



Expand All @@ -59,8 +63,8 @@

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| store_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | |
| hot_ranges | [HotRangesResponse.HotRange](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRange) | repeated | |
| store_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | StoreID identifies the store for which the report was produced. |
| hot_ranges | [HotRangesResponse.HotRange](#cockroach.server.serverpb.HotRangesResponse-cockroach.server.serverpb.HotRangesResponse.HotRange) | repeated | HotRanges is the hot ranges report for this store on the target node. |



Expand All @@ -71,8 +75,10 @@

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.server.serverpb.HotRangesResponse-cockroach.roachpb.RangeDescriptor) | | |
| queries_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.server.serverpb.HotRangesResponse-cockroach.roachpb.RangeDescriptor) | | Desc is the descriptor of the range for which the report was produced.

Note: this field is generally RESERVED and will likely be removed or replaced in a later version. See: https://github.com/cockroachdb/cockroach/issues/53212 |
| queries_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | QueriesPerSecond is the recent number of queries per second on this range. |



Expand Down
2 changes: 2 additions & 0 deletions docs/generated/sql/aggregates.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@
</span></td></tr>
<tr><td><a name="st_makeline"></a><code>st_makeline(arg1: geometry) &rarr; geometry</code></td><td><span class="funcdesc"><p>Forms a LineString from Point, MultiPoint or LineStrings. Other shapes will be ignored.</p>
</span></td></tr>
<tr><td><a name="st_union"></a><code>st_union(arg1: geometry) &rarr; geometry</code></td><td><span class="funcdesc"><p>Applies a spatial union to the geometries provided.</p>
</span></td></tr>
<tr><td><a name="stddev"></a><code>stddev(arg1: <a href="decimal.html">decimal</a>) &rarr; <a href="decimal.html">decimal</a></code></td><td><span class="funcdesc"><p>Calculates the standard deviation of the selected values.</p>
</span></td></tr>
<tr><td><a name="stddev"></a><code>stddev(arg1: <a href="float.html">float</a>) &rarr; <a href="float.html">float</a></code></td><td><span class="funcdesc"><p>Calculates the standard deviation of the selected values.</p>
Expand Down
15 changes: 10 additions & 5 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,10 @@ has no relationship with the commit order of concurrent transactions.</p>
<tr><td><a name="st_azimuth"></a><code>st_azimuth(geometry_a: geometry, geometry_b: geometry) &rarr; <a href="float.html">float</a></code></td><td><span class="funcdesc"><p>Returns the azimuth in radians of the segment defined by the given point geometries, or NULL if the two points are coincident.</p>
<p>The azimuth is angle is referenced from north, and is positive clockwise: North = 0; East = π/2; South = π; West = 3π/2.</p>
</span></td></tr>
<tr><td><a name="st_box2dfromgeohash"></a><code>st_box2dfromgeohash(geohash: <a href="string.html">string</a>) &rarr; box2d</code></td><td><span class="funcdesc"><p>Return a Box2D from a GeoHash string with max precision.</p>
</span></td></tr>
<tr><td><a name="st_box2dfromgeohash"></a><code>st_box2dfromgeohash(geohash: <a href="string.html">string</a>, precision: <a href="int.html">int</a>) &rarr; box2d</code></td><td><span class="funcdesc"><p>Return a Box2D from a GeoHash string with supplied precision.</p>
</span></td></tr>
<tr><td><a name="st_buffer"></a><code>st_buffer(geography: geography, distance: <a href="float.html">float</a>) &rarr; geography</code></td><td><span class="funcdesc"><p>Returns a Geometry that represents all points whose distance is less than or equal to the given distance
from the given Geometry.</p>
<p>This function utilizes the GEOS module.</p>
Expand Down Expand Up @@ -1264,6 +1268,10 @@ Bottom Left.</p>
</span></td></tr>
<tr><td><a name="st_geomfromewkt"></a><code>st_geomfromewkt(val: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from an EWKT representation.</p>
</span></td></tr>
<tr><td><a name="st_geomfromgeohash"></a><code>st_geomfromgeohash(geohash: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a POLYGON Geometry from a GeoHash string with max precision.</p>
</span></td></tr>
<tr><td><a name="st_geomfromgeohash"></a><code>st_geomfromgeohash(geohash: <a href="string.html">string</a>, precision: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a POLYGON Geometry from a GeoHash string with supplied precision.</p>
</span></td></tr>
<tr><td><a name="st_geomfromgeojson"></a><code>st_geomfromgeojson(val: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from an GeoJSON representation.</p>
</span></td></tr>
<tr><td><a name="st_geomfromgeojson"></a><code>st_geomfromgeojson(val: jsonb) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from an GeoJSON representation.</p>
Expand Down Expand Up @@ -1486,9 +1494,9 @@ calculated, the result is transformed back into a Geography with SRID 4326.</p>
</span></td></tr>
<tr><td><a name="st_point"></a><code>st_point(x: <a href="float.html">float</a>, y: <a href="float.html">float</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a new Point with the given X and Y coordinates.</p>
</span></td></tr>
<tr><td><a name="st_pointfromgeohash"></a><code>st_pointfromgeohash(geohash: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a Geometry point from a GeoHash string with max precision.</p>
<tr><td><a name="st_pointfromgeohash"></a><code>st_pointfromgeohash(geohash: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a POINT Geometry from a GeoHash string with max precision.</p>
</span></td></tr>
<tr><td><a name="st_pointfromgeohash"></a><code>st_pointfromgeohash(geohash: <a href="string.html">string</a>, precision: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a Geometry point from a GeoHash string with supplied precision.</p>
<tr><td><a name="st_pointfromgeohash"></a><code>st_pointfromgeohash(geohash: <a href="string.html">string</a>, precision: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Return a POINT Geometry from a GeoHash string with supplied precision.</p>
</span></td></tr>
<tr><td><a name="st_pointfromtext"></a><code>st_pointfromtext(str: <a href="string.html">string</a>, srid: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from a WKT or EWKT representation with an SRID. If the shape underneath is not Point, NULL is returned. If the SRID is present in both the EWKT and the argument, the argument value is used.</p>
</span></td></tr>
Expand Down Expand Up @@ -1601,9 +1609,6 @@ Negative azimuth values and values greater than 2π (360 degrees) are supported.
</span></td></tr>
<tr><td><a name="st_translate"></a><code>st_translate(g: geometry, deltaX: <a href="float.html">float</a>, deltaY: <a href="float.html">float</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns a modified Geometry translated by the given deltas</p>
</span></td></tr>
<tr><td><a name="st_union"></a><code>st_union(geometry_a: geometry, geometry_b: geometry) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the union of the given geometries as a single Geometry object.</p>
<p>This function utilizes the GEOS module.</p>
</span></td></tr>
<tr><td><a name="st_within"></a><code>st_within(geometry_a: geometry, geometry_b: geometry) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>Returns true if geometry_a is completely inside geometry_b.</p>
<p>This function utilizes the GEOS module.</p>
<p>This function variant will attempt to utilize any available geospatial index.</p>
Expand Down
41 changes: 0 additions & 41 deletions pkg/geo/geohash.go

This file was deleted.

51 changes: 51 additions & 0 deletions pkg/geo/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"github.com/cockroachdb/cockroach/pkg/geo/geopb"
"github.com/cockroachdb/cockroach/pkg/geo/geos"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/errors"
"github.com/pierrre/geohash"
"github.com/twpayne/go-geom"
"github.com/twpayne/go-geom/encoding/ewkb"
"github.com/twpayne/go-geom/encoding/ewkbhex"
Expand Down Expand Up @@ -209,3 +211,52 @@ func hasPrefixIgnoreCase(str string, prefix string) bool {
}
return true
}

// ParseGeometryPointFromGeoHash converts a GeoHash to a Geometry Point
// using a Lng/Lat Point representation of the GeoHash.
func ParseGeometryPointFromGeoHash(g string, precision int) (*Geometry, error) {
box, err := parseGeoHash(g, precision)
if err != nil {
return nil, err
}
point := box.Center()
geom, gErr := NewGeometryFromPointCoords(point.Lon, point.Lat)
if gErr != nil {
return nil, gErr
}
return geom, nil
}

// ParseCartesianBoundingBoxFromGeoHash converts a GeoHash to a CartesianBoundingBox.
func ParseCartesianBoundingBoxFromGeoHash(g string, precision int) (CartesianBoundingBox, error) {
box, err := parseGeoHash(g, precision)
if err != nil {
return CartesianBoundingBox{}, err
}
return CartesianBoundingBox{
BoundingBox: geopb.BoundingBox{
LoX: box.Lon.Min,
HiX: box.Lon.Max,
LoY: box.Lat.Min,
HiY: box.Lat.Max,
},
}, nil
}

func parseGeoHash(g string, precision int) (geohash.Box, error) {
if len(g) == 0 {
return geohash.Box{}, errors.Newf("length of GeoHash must be greater than 0")
}

// If precision is more than the length of the geohash
// or if precision is less than 0 then set
// precision equal to length of geohash.
if precision > len(g) || precision < 0 {
precision = len(g)
}
box, err := geohash.Decode(g[:precision])
if err != nil {
return geohash.Box{}, err
}
return box, nil
}
82 changes: 82 additions & 0 deletions pkg/geo/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
package geo

import (
"fmt"
"testing"

"github.com/cockroachdb/cockroach/pkg/geo/geopb"
"github.com/pierrre/geohash"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -589,3 +591,83 @@ func TestParseGeography(t *testing.T) {
})
}
}

func TestParseHash(t *testing.T) {
testCases := []struct {
h string
p int
expected geohash.Box
}{
{"123", 2, geohash.Box{
Lat: geohash.Range{
Min: -90,
Max: -84.375,
},
Lon: geohash.Range{
Min: -123.75,
Max: -112.5,
},
}},
{"123", 3, geohash.Box{
Lat: geohash.Range{
Min: -88.59375,
Max: -87.1875,
},
Lon: geohash.Range{
Min: -122.34375,
Max: -120.9375,
},
}},
{"123", 4, geohash.Box{
Lat: geohash.Range{
Min: -88.59375,
Max: -87.1875,
},
Lon: geohash.Range{
Min: -122.34375,
Max: -120.9375,
},
}},
{"123", -1, geohash.Box{
Lat: geohash.Range{
Min: -88.59375,
Max: -87.1875,
},
Lon: geohash.Range{
Min: -122.34375,
Max: -120.9375,
},
}},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%s[:%d]", tc.h, tc.p), func(t *testing.T) {
ret, err := parseGeoHash(tc.h, tc.p)
require.NoError(t, err)
require.Equal(t, tc.expected, ret)
})
}

errorCases := []struct {
h string
p int
err string
}{
{
"",
10,
"length of GeoHash must be greater than 0",
},
{
"-",
10,
`geohash decode '-': invalid character at index 0`,
},
}
for _, tc := range errorCases {
t.Run(fmt.Sprintf("%s[:%d]", tc.h, tc.p), func(t *testing.T) {
_, err := parseGeoHash(tc.h, tc.p)
require.Error(t, err)
require.EqualError(t, err, tc.err)
})
}
}
Loading

0 comments on commit f55266f

Please sign in to comment.