From c35b276bbfe01154c768c58172641df11ab1f9f7 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Wed, 22 Oct 2014 14:22:25 +0000 Subject: [PATCH] Implement interruptability of ST_Segmentize(geometry) Includes testcases at sql and cunit levels. Closes #2893 git-svn-id: http://svn.osgeo.org/postgis/trunk@13105 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 2 ++ liblwgeom/cunit/cu_measures.c | 40 ++++++++++++++++++++++++++++++++ liblwgeom/lwcollection.c | 7 ++++++ liblwgeom/lwline.c | 5 ++-- liblwgeom/lwpoly.c | 5 ++++ liblwgeom/ptarray.c | 2 ++ libpgcommon/lwgeom_pg.h | 5 ++++ postgis/lwgeom_functions_basic.c | 9 ++++++- regress/Makefile.in | 1 + regress/interrupt.sql | 14 +++++++++++ regress/interrupt_expected | 4 ++++ 11 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 regress/interrupt.sql create mode 100644 regress/interrupt_expected diff --git a/NEWS b/NEWS index 060831111..162da5d6d 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,8 @@ PostGIS 2.2.0 * Bug Fixes * + - #2893, Allow interruptibility of ST_Segmentize(geometry) + (Sandro Santilli / CartoDB) - #2540, Change GUC name for GDAL_DATA to postgis.gdal_datapath - #2777, Raster max extent constraint based upon envelope to behave like geometry extent diff --git a/liblwgeom/cunit/cu_measures.c b/liblwgeom/cunit/cu_measures.c index 14487e339..20af13fca 100644 --- a/liblwgeom/cunit/cu_measures.c +++ b/liblwgeom/cunit/cu_measures.c @@ -409,6 +409,46 @@ test_lwgeom_segmentize2d(void) lwfree(strout); lwgeom_free(linein); lwgeom_free(lineout); + + /* test interruption */ + + linein = lwgeom_from_wkt("LINESTRING(0 0,10 0)", LW_PARSER_CHECK_NONE); + lwgeom_request_interrupt(); + lineout = lwgeom_segmentize2d(linein, 1e-100); + CU_ASSERT_EQUAL(lineout, NULL); + lwgeom_free(linein); + + linein = lwgeom_from_wkt("MULTILINESTRING((0 0,10 0),(20 0, 30 0))", LW_PARSER_CHECK_NONE); + lwgeom_request_interrupt(); + lineout = lwgeom_segmentize2d(linein, 1e-100); + CU_ASSERT_EQUAL(lineout, NULL); + lwgeom_free(linein); + + linein = lwgeom_from_wkt( +"MULTIPOLYGON(((0 0,20 0,20 20,0 20,0 0),(2 2,2 4,4 4,4 2,2 2),(6 6,6 8,8 8,8 6,6 6)),((40 0,40 20,60 20,60 0,40 0),(42 2,42 4,44 4,44 2,42 2)))" + , LW_PARSER_CHECK_NONE); + lwgeom_request_interrupt(); + lineout = lwgeom_segmentize2d(linein, 1e-100); + CU_ASSERT_EQUAL(lineout, NULL); + lwgeom_free(linein); + + linein = lwgeom_from_wkt( +"GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0,20 0,20 20,0 20,0 0),(2 2,2 4,4 4,4 2,2 2),(6 6,6 8,8 8,8 6,6 6)),((40 0,40 20,60 20,60 0,40 0),(42 2,42 4,44 4,44 2,42 2))),MULTILINESTRING((0 0,10 0),(20 0, 30 0)),MULTIPOINT(0 0, 3 4))" + , LW_PARSER_CHECK_NONE); + CU_ASSERT_FATAL(linein != NULL); + lwgeom_request_interrupt(); + lineout = lwgeom_segmentize2d(linein, 1e-100); + CU_ASSERT_EQUAL(lineout, NULL); + lwgeom_free(linein); + + linein = lwgeom_from_wkt("LINESTRING(20 0, 30 0)", LW_PARSER_CHECK_NONE); + /* NOT INTERRUPTED */ + lineout = lwgeom_segmentize2d(linein, 5); + strout = lwgeom_to_ewkt(lineout); + CU_ASSERT_STRING_EQUAL(strout, "LINESTRING(20 0,25 0,30 0)"); + lwfree(strout); + lwgeom_free(linein); + lwgeom_free(lineout); } static void diff --git a/liblwgeom/lwcollection.c b/liblwgeom/lwcollection.c index 68a6f800a..089ad61af 100644 --- a/liblwgeom/lwcollection.c +++ b/liblwgeom/lwcollection.c @@ -229,7 +229,14 @@ lwcollection_segmentize2d(LWCOLLECTION *col, double dist) newgeoms = lwalloc(sizeof(LWGEOM *)*col->ngeoms); for (i=0; ingeoms; i++) + { newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist); + if ( ! newgeoms[i] ) { + while (i--) lwgeom_free(newgeoms[i]); + lwfree(newgeoms); + return NULL; + } + } return lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, newgeoms); } diff --git a/liblwgeom/lwline.c b/liblwgeom/lwline.c index 570dea078..0fe0138a6 100644 --- a/liblwgeom/lwline.c +++ b/liblwgeom/lwline.c @@ -133,8 +133,9 @@ lwline_reverse(LWLINE *line) LWLINE * lwline_segmentize2d(LWLINE *line, double dist) { - return lwline_construct(line->srid, NULL, - ptarray_segmentize2d(line->points, dist)); + POINTARRAY *segmentized = ptarray_segmentize2d(line->points, dist); + if ( ! segmentized ) return NULL; + return lwline_construct(line->srid, NULL, segmentized); } /* check coordinate equality */ diff --git a/liblwgeom/lwpoly.c b/liblwgeom/lwpoly.c index de475793c..4954a3551 100644 --- a/liblwgeom/lwpoly.c +++ b/liblwgeom/lwpoly.c @@ -216,6 +216,11 @@ lwpoly_segmentize2d(LWPOLY *poly, double dist) for (i=0; inrings; i++) { newrings[i] = ptarray_segmentize2d(poly->rings[i], dist); + if ( ! newrings[i] ) { + while (i--) ptarray_free(newrings[i]); + lwfree(newrings); + return NULL; + } } return lwpoly_construct(poly->srid, NULL, poly->nrings, newrings); diff --git a/liblwgeom/ptarray.c b/liblwgeom/ptarray.c index af26b4afd..55abdf1b3 100644 --- a/liblwgeom/ptarray.c +++ b/liblwgeom/ptarray.c @@ -434,6 +434,8 @@ ptarray_segmentize2d(const POINTARRAY *ipa, double dist) p1 = p2; ipoff++; } + + LW_ON_INTERRUPT(ptarray_free(opa); return NULL); } return opa; diff --git a/libpgcommon/lwgeom_pg.h b/libpgcommon/lwgeom_pg.h index 94638c2b3..9de4eaff5 100644 --- a/libpgcommon/lwgeom_pg.h +++ b/libpgcommon/lwgeom_pg.h @@ -75,6 +75,11 @@ extern void pg_unparser_errhint(LWGEOM_UNPARSER_RESULT *lwg_unparser_result); pg_unparser_errhint(&lwg_unparser_result); \ } while(0); +/* TODO: only cancel the interrupt if inside an outer call ? */ +#define LWGEOM_INIT() { \ + lwgeom_cancel_interrupt(); \ +} + /* ** GSERIALIED prototypes used outside the index functions diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c index 66767d332..c78c6d61c 100644 --- a/postgis/lwgeom_functions_basic.c +++ b/postgis/lwgeom_functions_basic.c @@ -1904,15 +1904,22 @@ Datum LWGEOM_segmentize2d(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } + LWGEOM_INIT(); + inlwgeom = lwgeom_from_gserialized(ingeom); - if ( lwgeom_is_empty(inlwgeom) ) { + /* Should only happen on interruption */ lwgeom_free(inlwgeom); PG_RETURN_POINTER(ingeom); } outlwgeom = lwgeom_segmentize2d(inlwgeom, dist); + if ( ! outlwgeom ) { + /* Should only happen on interruption */ + PG_FREE_IF_COPY(ingeom, 0); + PG_RETURN_NULL(); + } /* Copy input bounding box if any */ if ( inlwgeom->bbox ) diff --git a/regress/Makefile.in b/regress/Makefile.in index fb425d8bb..b3d749cc4 100644 --- a/regress/Makefile.in +++ b/regress/Makefile.in @@ -108,6 +108,7 @@ TESTS = \ in_gml \ in_kml \ in_encodedpolyline \ + interrupt \ iscollection \ regress_ogc \ regress_ogc_cover \ diff --git a/regress/interrupt.sql b/regress/interrupt.sql new file mode 100644 index 000000000..16af1e144 --- /dev/null +++ b/regress/interrupt.sql @@ -0,0 +1,14 @@ +SET statement_timeout TO 100; + +select ST_Buffer(g,100) from ( + select (st_dumppoints(st_buffer(st_makepoint(0,0),10000,100000))).geom g +) foo; + +SELECT +ST_Segmentize(ST_MakeLine(ST_MakePoint(4,39), ST_MakePoint(1,41)), + 1e-100); + +SET statement_timeout TO 0; + +-- Not affected by timeout +SELECT '1',ST_AsText(ST_Segmentize('LINESTRING(0 0,4 0)'::geometry, 2)); diff --git a/regress/interrupt_expected b/regress/interrupt_expected new file mode 100644 index 000000000..d9fa03e21 --- /dev/null +++ b/regress/interrupt_expected @@ -0,0 +1,4 @@ +ERROR: canceling statement due to statement timeout +NOTICE: liblwgeom code interrupted +ERROR: canceling statement due to statement timeout +1|LINESTRING(0 0,2 0,4 0) -- 2.40.0