]> granicus.if.org Git - postgis/commitdiff
Equality test failed on points with same coordinates
authorPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 25 Oct 2017 13:26:11 +0000 (13:26 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 25 Oct 2017 13:26:11 +0000 (13:26 +0000)
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

liblwgeom/g_serialized.c
postgis/lwgeom_btree.c
regress/tickets.sql
regress/tickets_expected

index 92f4a91ced879ba1b86b9d790012189806f9e58b..4b43cfb84872b12d8b992c970f6785d6ac433107 100644 (file)
@@ -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)
index b937bfa5aa4f0119e7fc48c90098af29ff992d0b..3ab2a319a869b3f564bf0cebdfe7410350d122df 100644 (file)
@@ -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);
 }
index fdaa8e171ed06336fc28ed9db1785e7aab0bb499..7b14257df1775a29438b2e9af9fa6d890a6f95a3 100644 (file)
@@ -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;
 
 
index f1fd73e4e8a0de0a7b06b18078ec057116bada05..f15edcd84d651ac29e565e6266937fa407a0b877 100644 (file)
@@ -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)