From b700b31b6b2b63ae8cc08ac5b5345373265c464c Mon Sep 17 00:00:00 2001 From: Daniel Baston Date: Mon, 7 Dec 2015 22:12:31 +0000 Subject: [PATCH] #3382, avoid deserializing small geometries during index ops git-svn-id: http://svn.osgeo.org/postgis/trunk@14478 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/cunit/cu_libgeom.c | 121 +++++++++++++++++++++++++++++++++- liblwgeom/g_serialized.c | 19 +++++- postgis/gserialized_gist_2d.c | 10 +-- 3 files changed, 141 insertions(+), 9 deletions(-) diff --git a/liblwgeom/cunit/cu_libgeom.c b/liblwgeom/cunit/cu_libgeom.c index accf7d2a7..aa061f1b7 100644 --- a/liblwgeom/cunit/cu_libgeom.c +++ b/liblwgeom/cunit/cu_libgeom.c @@ -15,6 +15,7 @@ #include "CUnit/Basic.h" #include "liblwgeom_internal.h" +#include "g_serialized.c" /* for gserialized_peek_gbox_p */ #include "cu_tester.h" static void test_typmod_macros(void) @@ -1072,6 +1073,121 @@ void test_gbox_same_2d(void) lwfree(s3); } +void test_gserialized_peek_gbox_p_no_box_when_empty(void); +void test_gserialized_peek_gbox_p_no_box_when_empty(void) +{ + uint32_t i; + + char *ewkt[] = + { + "POINT EMPTY", + "LINESTRING EMPTY", + "MULTIPOINT EMPTY", + "MULTIPOINT (EMPTY)", + "MULTILINESTRING EMPTY", + "MULTILINESTRING (EMPTY)" + }; + + for ( i = 0; i < (sizeof ewkt/sizeof(char*)); i++ ) + { + LWGEOM* geom = lwgeom_from_wkt(ewkt[i], LW_PARSER_CHECK_NONE); + GBOX box; + gbox_init(&box); + + GSERIALIZED* gser = gserialized_from_lwgeom(geom, NULL); + + CU_ASSERT_FALSE(gserialized_has_bbox(gser)); + + CU_ASSERT_EQUAL(LW_FAILURE, gserialized_peek_gbox_p(gser, &box)); + + lwgeom_free(geom); + lwfree(gser); + } +} + +void test_gserialized_peek_gbox_p_gets_correct_box(void); +void test_gserialized_peek_gbox_p_gets_correct_box(void) +{ + uint32_t i; + + char *ewkt[] = + { + "POINT (2.2945672355 48.85822923236)", + "POINTZ (2.2945672355 48.85822923236 15)", + "POINTM (2.2945672355 48.85822923236 12)", + "POINT ZM (2.2945672355 48.85822923236 12 2)", + "MULTIPOINT ((-76.45402132523 44.225406213532))", + "MULTIPOINT Z ((-76.45402132523 44.225406213532 112))", + "MULTIPOINT ZM ((-76.45402132523 44.225406213532 112 44))", + "LINESTRING (2.2945672355 48.85822923236, -76.45402132523 44.225406213532)", + "LINESTRING Z (2.2945672355 48.85822923236 6, -76.45402132523 44.225406213532 8)", + "LINESTRING ZM (2.2945672355 48.85822923236 3 2, -76.45402132523 44.225406213532 9 4)", + "MULTILINESTRING ((2.2945672355 48.85822923236, -76.45402132523 44.225406213532))", + "MULTILINESTRING Z ((2.2945672355 48.85822923236 4, -76.45402132523 44.225406213532 3))" + }; + + for ( i = 0; i < (sizeof ewkt/sizeof(char*)); i++ ) + { + LWGEOM* geom = lwgeom_from_wkt(ewkt[i], LW_PARSER_CHECK_NONE); + GBOX box_from_peek; + GBOX box_from_lwgeom; + gbox_init(&box_from_peek); + gbox_init(&box_from_lwgeom); + + GSERIALIZED* gser = gserialized_from_lwgeom(geom, NULL); + + CU_ASSERT_FALSE(gserialized_has_bbox(gser)); + + lwgeom_calculate_gbox(geom, &box_from_lwgeom); + gserialized_peek_gbox_p(gser, &box_from_peek); + + gbox_float_round(&box_from_lwgeom); + + CU_ASSERT_TRUE(gbox_same(&box_from_peek, &box_from_lwgeom)); + + lwgeom_free(geom); + lwfree(gser); + } +} + +void test_gserialized_peek_gbox_p_fails_for_unsupported_cases(void); +void test_gserialized_peek_gbox_p_fails_for_unsupported_cases(void) +{ + uint32_t i; + + char *ewkt[] = + { + "MULTIPOINT ((-76.45402132523 44.225406213532), (-72 33))", + "LINESTRING (2.2945672355 48.85822923236, -76.45402132523 44.225406213532, -72 33)", + "MULTILINESTRING ((2.2945672355 48.85822923236, -76.45402132523 44.225406213532, -72 33))", + "MULTILINESTRING ((2.2945672355 48.85822923236, -76.45402132523 44.225406213532), (-72 33, -71 32))" + }; + + for ( i = 0; i < (sizeof ewkt/sizeof(char*)); i++ ) + { + LWGEOM* geom = lwgeom_from_wkt(ewkt[i], LW_PARSER_CHECK_NONE); + GBOX box; + gbox_init(&box); + lwgeom_drop_bbox(geom); + + /* Construct a GSERIALIZED* that doesn't have a box, so that we can test the + * actual logic of the peek function */ + size_t expected_size = gserialized_from_lwgeom_size(geom); + GSERIALIZED* gser = lwalloc(expected_size); + uint8_t* ptr = (uint8_t*) gser; + + ptr += 8; // Skip header + gserialized_from_lwgeom_any(geom, ptr); + gser->flags = geom->flags; + + CU_ASSERT_FALSE(gserialized_has_bbox(gser)); + CU_ASSERT_EQUAL(LW_FAILURE, gserialized_peek_gbox_p(gser, &box)); + + lwgeom_free(geom); + lwfree(gser); + } +} + /* ** Used by test harness to register the tests in this file. */ @@ -1101,5 +1217,8 @@ void libgeom_suite_setup(void) PG_ADD_TEST(suite, test_lwgeom_as_curve); PG_ADD_TEST(suite, test_lwgeom_scale); PG_ADD_TEST(suite, test_gserialized_is_empty); - PG_ADD_TEST(suite, test_gbox_same_2d); + PG_ADD_TEST(suite, test_gserialized_peek_gbox_p_no_box_when_empty); + PG_ADD_TEST(suite, test_gserialized_peek_gbox_p_gets_correct_box); + PG_ADD_TEST(suite, test_gserialized_peek_gbox_p_fails_for_unsupported_cases); + PG_ADD_TEST(suite, test_gbox_same_2d); } diff --git a/liblwgeom/g_serialized.c b/liblwgeom/g_serialized.c index a5aa99961..c57810703 100644 --- a/liblwgeom/g_serialized.c +++ b/liblwgeom/g_serialized.c @@ -227,6 +227,7 @@ static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; + gbox->flags = g->flags; if ( FLAGS_GET_Z(g->flags) ) { gbox->zmin = gbox->zmax = dptr[i++]; @@ -262,6 +263,7 @@ static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); + gbox->flags = g->flags; if ( FLAGS_GET_Z(g->flags) ) { /* Advance to Z */ @@ -286,19 +288,29 @@ static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) double *dptr = (double*)(g->data); int *iptr = (int*)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ - + int npoints; + /* This only works with single-entry multipoints */ if ( ngeoms != 1 ) return LW_FAILURE; + /* Npoints is at */ + npoints = iptr[3]; + + /* The check below is necessary because we can have a MULTIPOINT + * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */ + if ( npoints != 1 ) + return LW_FAILURE; + /* Move forward two doubles (four ints) */ /* Past */ - /* Past */ + /* Past */ i += 2; /* Read the doubles from the one point */ gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; + gbox->flags = g->flags; if ( FLAGS_GET_Z(g->flags) ) { gbox->zmin = gbox->zmax = dptr[i++]; @@ -319,7 +331,7 @@ static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) int *iptr = (int*)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; - + /* This only works with 1-line multilines */ if ( ngeoms != 1 ) return LW_FAILURE; @@ -343,6 +355,7 @@ static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); + gbox->flags = g->flags; if ( FLAGS_GET_Z(g->flags) ) { /* Advance to Z */ diff --git a/postgis/gserialized_gist_2d.c b/postgis/gserialized_gist_2d.c index eb0878181..badfab439 100644 --- a/postgis/gserialized_gist_2d.c +++ b/postgis/gserialized_gist_2d.c @@ -565,17 +565,17 @@ gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df) /* No, we need to calculate it from the full object. */ GBOX gbox; GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum); - LWGEOM *lwgeom = lwgeom_from_gserialized(g); - if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) + + gbox_init(&gbox); + + if (gserialized_get_gbox_p(g, &gbox) == LW_FAILURE) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); - lwgeom_free(lwgeom); return LW_FAILURE; } - lwgeom_free(lwgeom); result = box2df_from_gbox_p(&gbox, box2df); } - + if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got box2df %s", box2df_to_string(box2df)); -- 2.40.0