From: Paul Ramsey Date: Wed, 25 Oct 2017 13:26:11 +0000 (+0000) Subject: Equality test failed on points with same coordinates X-Git-Tag: 2.5.0alpha~348 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=699fddf0cf254cd01d154ba2e5d5055e94f82efe;p=postgis Equality test failed on points with same coordinates and differing SRIDs, resulting in incorrect hashing. Amazingly, this problem existed in *both* the btree function and the new hash machinery (and it was the btree machinery that was actually called for the UNION operation). References #1014 git-svn-id: http://svn.osgeo.org/postgis/trunk@16059 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/g_serialized.c b/liblwgeom/g_serialized.c index 92f4a91ce..4b43cfb84 100644 --- a/liblwgeom/g_serialized.c +++ b/liblwgeom/g_serialized.c @@ -305,10 +305,10 @@ int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2) if ( sz1 > 16 && // 16 is size of EMPTY, if it's larger - it has coordinates sz2 > 16 && - *(uint32_t*)(g1 + 2 * sizeof(uint32_t)) == POINTTYPE && - *(uint32_t*)(g2 + 2 * sizeof(uint32_t)) == POINTTYPE && !FLAGS_GET_BBOX(g1->flags) && - !FLAGS_GET_BBOX(g2->flags) + !FLAGS_GET_BBOX(g2->flags) && + *(uint32_t*)(g1->data) == POINTTYPE && + *(uint32_t*)(g2->data) == POINTTYPE ) { double *dptr = (double*)(g1->data + sizeof(double)); @@ -321,12 +321,18 @@ int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2) y.f = 2.0 * dptr[1]; hash2 = uint32_interleave_2(x.u, y.u); - if ( hash1 > hash2 ) - return 1; - if ( hash1 < hash2 ) - return -1; + /* If the SRIDs are the same, we can use hash inequality */ + /* to jump us out of this function early. Otherwise we still */ + /* have to do the full calculation */ + if ( gserialized_cmp_srid(g1, g2) == 0 ) + { + if ( hash1 > hash2 ) + return 1; + if ( hash1 < hash2 ) + return -1; + } - // if hashes happen to be the same, go to full compare. + /* if hashes happen to be the same, go to full compare. */ } size_t hsz1 = gserialized_header_size(g1); @@ -401,7 +407,7 @@ int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2) else if (bsz1 > bsz2) return 1; } - return cmp == 0 ? 0 : (cmp > 0 ? 1 : -1); + return cmp > 0 ? 1 : -1; } int gserialized_read_gbox_p(const GSERIALIZED *g, GBOX *gbox) diff --git a/postgis/lwgeom_btree.c b/postgis/lwgeom_btree.c index b937bfa5a..3ab2a319a 100644 --- a/postgis/lwgeom_btree.c +++ b/postgis/lwgeom_btree.c @@ -132,11 +132,23 @@ PG_FUNCTION_INFO_V1(lwgeom_hash); Datum lwgeom_hash(PG_FUNCTION_ARGS) { GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0); - size_t sz1 = VARSIZE(g1); + /* Point to just the type/coordinate part of buffer */ size_t hsz1 = gserialized_header_size(g1); uint8_t *b1 = (uint8_t*)g1 + hsz1; + /* Calculate size of type/coordinate buffer */ + size_t sz1 = VARSIZE(g1); size_t bsz1 = sz1 - hsz1; - Datum hval = hash_any(b1, bsz1); + /* Calculate size of srid/type/coordinate buffer */ + int srid = gserialized_get_srid(g1); + size_t bsz2 = bsz1 + sizeof(int); + uint8_t *b2 = palloc(bsz2); + /* Copy srid into front of combined buffer */ + memcpy(b2, &srid, sizeof(int)); + /* Copy type/coordinates into rest of combined buffer */ + memcpy(b2+sizeof(int), b1, bsz1); + /* Hash combined buffer */ + Datum hval = hash_any(b2, bsz2); + pfree(b2); PG_FREE_IF_COPY(g1, 0); PG_RETURN_DATUM(hval); } diff --git a/regress/tickets.sql b/regress/tickets.sql index fdaa8e171..7b14257df 100644 --- a/regress/tickets.sql +++ b/regress/tickets.sql @@ -1048,6 +1048,11 @@ WITH RECURSIVE path (id, g) AS ( WHERE ST_Y(path.g) = ST_X(rec.g) ) SELECT '#1014c', id, st_astext(g) FROM path; +SELECT '#1014d', ST_AsEWKT(g) FROM ( + SELECT 'SRID=1;POINT(0 1)'::geometry AS g + UNION + SELECT 'SRID=2;POINT(0 1)'::geometry AS g +) a; DROP TABLE IF EXISTS rec; diff --git a/regress/tickets_expected b/regress/tickets_expected index f1fd73e4e..f15edcd84 100644 --- a/regress/tickets_expected +++ b/regress/tickets_expected @@ -312,3 +312,5 @@ ERROR: invalid KML representation #1014c|1|POINT(0 1) #1014c|2|POINT(1 2) #1014c|3|POINT(2 3) +#1014d|SRID=1;POINT(0 1) +#1014d|SRID=2;POINT(0 1)