[postgis-tickets] r17593 - Speed up ST_X, ST_Y, ST_Z and ST_M
Raul
raul at rmr.ninja
Fri Jul 12 09:03:58 PDT 2019
Author: algunenano
Date: 2019-07-12 09:03:58 -0700 (Fri, 12 Jul 2019)
New Revision: 17593
Added:
trunk/regress/core/point_coordinates.sql
trunk/regress/core/point_coordinates_expected
Modified:
trunk/NEWS
trunk/liblwgeom/cunit/cu_gserialized1.c
trunk/liblwgeom/cunit/cu_gserialized2.c
trunk/liblwgeom/gserialized.c
trunk/liblwgeom/gserialized.h
trunk/liblwgeom/gserialized1.c
trunk/liblwgeom/gserialized1.h
trunk/liblwgeom/gserialized2.c
trunk/liblwgeom/gserialized2.h
trunk/liblwgeom/liblwgeom.h.in
trunk/postgis/lwgeom_ogc.c
trunk/regress/core/Makefile.in
Log:
Speed up ST_X, ST_Y, ST_Z and ST_M
Closes https://github.com/postgis/postgis/pull/435
Closes https://trac.osgeo.org/postgis/ticket/4449
Modified: trunk/NEWS
===================================================================
--- trunk/NEWS 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/NEWS 2019-07-12 16:03:58 UTC (rev 17593)
@@ -11,6 +11,7 @@
- #4450, Speed up ST_GeometryType (Raúl Marín)
- #4452, Add ST_TileEnvelope() (Paul Ramsey)
- #4417, Update spatial_ref_sys with new entries (Paul Ramsey)
+ - #4449, Speed up ST_X, ST_Y, ST_Z and ST_M (Raúl Marín)
PostGIS 3.0.0alpha3
Modified: trunk/liblwgeom/cunit/cu_gserialized1.c
===================================================================
--- trunk/liblwgeom/cunit/cu_gserialized1.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/cunit/cu_gserialized1.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -1204,6 +1204,54 @@
CU_ASSERT_EQUAL(SIGNUM(-10) * 5, -5);
}
+static int
+peek1_point_helper(char *geometry, POINT4D *p)
+{
+ cu_error_msg_reset();
+ p->x = p->y = p->z = p->m = 0;
+ LWGEOM *geom = lwgeom_from_wkt(geometry, LW_PARSER_CHECK_NONE);
+ CU_ASSERT(geom != NULL);
+ GSERIALIZED *g = gserialized1_from_lwgeom(geom, NULL);
+ CU_ASSERT(g != NULL);
+
+ int ret = gserialized1_peek_first_point(g, p);
+ lwfree(g);
+ lwgeom_free(geom);
+
+ return ret;
+}
+
+static void
+test_gserialized1_peek_first_point(void)
+{
+ POINT4D p = {0};
+
+ CU_ASSERT(peek1_point_helper("POINT(1 2)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 1);
+ CU_ASSERT_EQUAL(p.y, 2);
+
+ CU_ASSERT(peek1_point_helper("POINTZ(10 20 30)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 10);
+ CU_ASSERT_EQUAL(p.y, 20);
+ CU_ASSERT_EQUAL(p.z, 30);
+
+ CU_ASSERT(peek1_point_helper("POINTM(100 200 300)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 100);
+ CU_ASSERT_EQUAL(p.y, 200);
+ CU_ASSERT_EQUAL(p.m, 300);
+
+ CU_ASSERT(peek1_point_helper("POINTZM(1000 2000 3000 4000)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 1000);
+ CU_ASSERT_EQUAL(p.y, 2000);
+ CU_ASSERT_EQUAL(p.z, 3000);
+ CU_ASSERT_EQUAL(p.m, 4000);
+
+ CU_ASSERT(peek1_point_helper("MULTIPOINT((0 0), (1 1))", &p) == LW_FAILURE);
+ CU_ASSERT(peek1_point_helper("LINESTRING(0 0, 1 1)", &p) == LW_FAILURE);
+ CU_ASSERT(peek1_point_helper("MULTILINESTRING((0 0, 1 1), (0 0, 1 1))", &p) == LW_FAILURE);
+ CU_ASSERT(peek1_point_helper("POLYGON((0 0, 1 1, 1 0, 0 0))", &p) == LW_FAILURE);
+}
+
/*
** Used by test harness to register the tests in this file.
*/
@@ -1237,4 +1285,5 @@
PG_ADD_TEST(suite, test_gserialized1_peek_gbox_p_fails_for_unsupported_cases);
PG_ADD_TEST(suite, test_gbox_same_2d);
PG_ADD_TEST(suite, test_signum_macro);
+ PG_ADD_TEST(suite, test_gserialized1_peek_first_point);
}
Modified: trunk/liblwgeom/cunit/cu_gserialized2.c
===================================================================
--- trunk/liblwgeom/cunit/cu_gserialized2.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/cunit/cu_gserialized2.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -447,6 +447,54 @@
}
}
+static int
+peek2_point_helper(char *geometry, POINT4D *p)
+{
+ cu_error_msg_reset();
+ p->x = p->y = p->z = p->m = 0;
+ LWGEOM *geom = lwgeom_from_wkt(geometry, LW_PARSER_CHECK_NONE);
+ CU_ASSERT(geom != NULL);
+ GSERIALIZED *g = gserialized2_from_lwgeom(geom, NULL);
+ CU_ASSERT(g != NULL);
+
+ int ret = gserialized2_peek_first_point(g, p);
+ lwfree(g);
+ lwgeom_free(geom);
+
+ return ret;
+}
+
+static void
+test_gserialized2_peek_first_point(void)
+{
+ POINT4D p = {0};
+
+ CU_ASSERT(peek2_point_helper("POINT(1 2)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 1);
+ CU_ASSERT_EQUAL(p.y, 2);
+
+ CU_ASSERT(peek2_point_helper("POINTZ(10 20 30)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 10);
+ CU_ASSERT_EQUAL(p.y, 20);
+ CU_ASSERT_EQUAL(p.z, 30);
+
+ CU_ASSERT(peek2_point_helper("POINTM(100 200 300)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 100);
+ CU_ASSERT_EQUAL(p.y, 200);
+ CU_ASSERT_EQUAL(p.m, 300);
+
+ CU_ASSERT(peek2_point_helper("POINTZM(1000 2000 3000 4000)", &p) == LW_SUCCESS);
+ CU_ASSERT_EQUAL(p.x, 1000);
+ CU_ASSERT_EQUAL(p.y, 2000);
+ CU_ASSERT_EQUAL(p.z, 3000);
+ CU_ASSERT_EQUAL(p.m, 4000);
+
+ CU_ASSERT(peek2_point_helper("MULTIPOINT((0 0), (1 1))", &p) == LW_FAILURE);
+ CU_ASSERT(peek2_point_helper("LINESTRING(0 0, 1 1)", &p) == LW_FAILURE);
+ CU_ASSERT(peek2_point_helper("MULTILINESTRING((0 0, 1 1), (0 0, 1 1))", &p) == LW_FAILURE);
+ CU_ASSERT(peek2_point_helper("POLYGON((0 0, 1 1, 1 0, 0 0))", &p) == LW_FAILURE);
+}
+
/*
** Used by test harness to register the tests in this file.
*/
@@ -464,4 +512,5 @@
PG_ADD_TEST(suite, test_gserialized2_peek_gbox_p_gets_correct_box);
PG_ADD_TEST(suite, test_gserialized2_peek_gbox_p_fails_for_unsupported_cases);
PG_ADD_TEST(suite, test_gserialized2_extended_flags);
+ PG_ADD_TEST(suite, test_gserialized2_peek_first_point);
}
Modified: trunk/liblwgeom/gserialized.c
===================================================================
--- trunk/liblwgeom/gserialized.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -253,9 +253,15 @@
return gserialized1_get_float_box_p(g, ndims);
}
+int
+gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
+{
+ if (GFLAGS_GET_VERSION(g->gflags))
+ return gserialized2_peek_first_point(g, out_point);
+ else
+ return gserialized1_peek_first_point(g, out_point);
+}
-
-
/**
* Return -1 if g1 is "less than" g2, 1 if g1 is "greater than"
* g2 and 0 if g1 and g2 are the "same". Equality is evaluated
Modified: trunk/liblwgeom/gserialized.h
===================================================================
--- trunk/liblwgeom/gserialized.h 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized.h 2019-07-12 16:03:58 UTC (rev 17593)
@@ -62,13 +62,13 @@
* Extract the geometry type from the serialized form (it hides in
* the anonymous data area, so this is a handy function).
*/
-uint32_t gserialized_get_type(const GSERIALIZED *g);
+extern uint32_t gserialized_get_type(const GSERIALIZED *g);
/**
* Returns the size in bytes to read from toast to get the basic
* information from a geometry: GSERIALIZED struct, bbox and type
*/
-uint32_t gserialized_max_header_size(void);
+extern uint32_t gserialized_max_header_size(void);
/**
* Returns a hash code for the srid/type/geometry information
@@ -75,19 +75,19 @@
* in the GSERIALIZED. Ignores metadata like flags and optional
* boxes, etc.
*/
-int32_t gserialized_hash(const GSERIALIZED *g);
+extern int32_t gserialized_hash(const GSERIALIZED *g);
/**
* Extract the SRID from the serialized form (it is packed into
* three bytes so this is a handy function).
*/
-int32_t gserialized_get_srid(const GSERIALIZED *g);
+extern int32_t gserialized_get_srid(const GSERIALIZED *g);
/**
* Write the SRID into the serialized form (it is packed into
* three bytes so this is a handy function).
*/
-void gserialized_set_srid(GSERIALIZED *g, int32_t srid);
+extern void gserialized_set_srid(GSERIALIZED *g, int32_t srid);
/**
* Check if a #GSERIALIZED is empty without deserializing first.
@@ -95,32 +95,32 @@
* is zero, will not catch collections of empty, eg:
* GEOMETRYCOLLECTION(POINT EMPTY)
*/
-int gserialized_is_empty(const GSERIALIZED *g);
+extern int gserialized_is_empty(const GSERIALIZED *g);
/**
* Check if a #GSERIALIZED has a bounding box without deserializing first.
*/
-int gserialized_has_bbox(const GSERIALIZED *gser);
+extern int gserialized_has_bbox(const GSERIALIZED *gser);
/**
* Check if a #GSERIALIZED has a Z ordinate.
*/
-int gserialized_has_z(const GSERIALIZED *gser);
+extern int gserialized_has_z(const GSERIALIZED *gser);
/**
* Check if a #GSERIALIZED has an M ordinate.
*/
-int gserialized_has_m(const GSERIALIZED *gser);
+extern int gserialized_has_m(const GSERIALIZED *gser);
/**
* Check if a #GSERIALIZED is a geography.
*/
-int gserialized_is_geodetic(const GSERIALIZED *gser);
+extern int gserialized_is_geodetic(const GSERIALIZED *gser);
/**
* Return the number of dimensions (2, 3, 4) in a geometry
*/
-int gserialized_ndims(const GSERIALIZED *gser);
+extern int gserialized_ndims(const GSERIALIZED *gser);
/**
* Return -1 if g1 is "less than" g2, 1 if g1 is "greater than"
@@ -131,7 +131,7 @@
* are evaluated by calculating a sortable key from the center
* point of the object bounds.
*/
-int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2);
+extern int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2);
/**
* Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding
@@ -166,3 +166,7 @@
*/
int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *box);
+/**
+ * Pull the first point values of a #GSERIALIZED. Only works for POINTTYPE
+ */
+int gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point);
Modified: trunk/liblwgeom/gserialized1.c
===================================================================
--- trunk/liblwgeom/gserialized1.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized1.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -470,6 +470,57 @@
return LW_FAILURE;
}
+static inline void
+gserialized1_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point)
+{
+ uint8_t dim = 0;
+ out_point->x = dptr[dim++];
+ out_point->y = dptr[dim++];
+
+ if (G1FLAGS_GET_Z(flags))
+ {
+ out_point->z = dptr[dim++];
+ }
+ if (G1FLAGS_GET_M(flags))
+ {
+ out_point->m = dptr[dim];
+ }
+}
+
+int
+gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
+{
+ uint8_t *geometry_start = ((uint8_t *)g->data);
+ if (gserialized1_has_bbox(g))
+ {
+ geometry_start += gserialized1_box_size(g);
+ }
+
+ uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0;
+ if (isEmpty)
+ {
+ return LW_FAILURE;
+ }
+
+ uint32_t type = (((uint32_t *)geometry_start)[0]);
+ /* Setup double_array_start depending on the geometry type */
+ double *double_array_start = NULL;
+ switch (type)
+ {
+ case (POINTTYPE):
+ /* For points we only need to jump over the type and npoints 32b ints */
+ double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t));
+ break;
+
+ default:
+ lwerror("%s is currently not implemented for type %d", __func__, type);
+ return LW_FAILURE;
+ }
+
+ gserialized1_copy_point(double_array_start, g->gflags, out_point);
+ return LW_SUCCESS;
+}
+
/**
* Read the bounding box off a serialization and calculate one if
* it is not already there.
Modified: trunk/liblwgeom/gserialized1.h
===================================================================
--- trunk/liblwgeom/gserialized1.h 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized1.h 2019-07-12 16:03:58 UTC (rev 17593)
@@ -160,3 +160,4 @@
int gserialized1_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox);
+int gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point);
\ No newline at end of file
Modified: trunk/liblwgeom/gserialized2.c
===================================================================
--- trunk/liblwgeom/gserialized2.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized2.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -99,6 +99,20 @@
return sz;
}
+/* Returns a pointer to the start of the geometry data */
+static inline uint8_t *
+gserialized2_get_geometry_p(const GSERIALIZED *g)
+{
+ uint32_t extra_data_bytes = 0;
+ if (gserialized2_has_extended(g))
+ extra_data_bytes += sizeof(uint64_t);
+
+ if (gserialized2_has_bbox(g))
+ extra_data_bytes += gserialized2_box_size(g);
+
+ return ((uint8_t *)g->data) + extra_data_bytes;
+}
+
uint8_t lwflags_get_g2flags(lwflags_t lwflags)
{
uint8_t gflags = 0;
@@ -169,8 +183,7 @@
uint32_t gserialized2_get_type(const GSERIALIZED *g)
{
- uint8_t *ptr = (uint8_t*)g;
- ptr += gserialized2_header_size(g);
+ uint8_t *ptr = gserialized2_get_geometry_p(g);
return *((uint32_t*)(ptr));
}
@@ -238,9 +251,7 @@
int gserialized2_is_empty(const GSERIALIZED *g)
{
int isempty = 0;
- uint8_t *p = (uint8_t*)g;
- assert(g);
- p += gserialized2_header_size(g);
+ uint8_t *p = gserialized2_get_geometry_p(g);
gserialized2_is_empty_recurse(p, &isempty);
return isempty;
}
@@ -349,8 +360,9 @@
gserialized2_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox)
{
uint32_t type = gserialized2_get_type(g);
- double *dptr = (double*)(g->data);
- int *iptr = (int*)(g->data);
+ uint8_t *geometry_start = gserialized2_get_geometry_p(g);
+ double *dptr = (double *)(geometry_start);
+ int32_t *iptr = (int32_t *)(geometry_start);
/* Peeking doesn't help if you already have a box or are geodetic */
if (G2FLAGS_GET_GEODETIC(g->gflags) || G2FLAGS_GET_BBOX(g->gflags))
@@ -357,18 +369,13 @@
{
return LW_FAILURE;
}
- /* Advance past 8 bytes of extended flags */
- if (G2FLAGS_GET_EXTENDED(g->gflags))
- {
- dptr += 1; /* doubleptr */
- iptr += 2; /* int32ptr */
- }
+
/* Boxes of points are easy peasy */
if (type == POINTTYPE)
{
int i = 1; /* Start past <pointtype><padding> */
- /* Read the empty flag */
+ /* Read the npoints flag */
int isempty = (iptr[1] == 0);
/* EMPTY point has no box */
@@ -520,6 +527,53 @@
return LW_FAILURE;
}
+static inline void
+gserialized2_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point)
+{
+ uint8_t dim = 0;
+ out_point->x = dptr[dim++];
+ out_point->y = dptr[dim++];
+
+ if (G2FLAGS_GET_Z(flags))
+ {
+ out_point->z = dptr[dim++];
+ }
+ if (G2FLAGS_GET_M(flags))
+ {
+ out_point->m = dptr[dim];
+ }
+}
+
+int
+gserialized2_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
+{
+ uint8_t *geometry_start = gserialized2_get_geometry_p(g);
+
+ uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0;
+ if (isEmpty)
+ {
+ return LW_FAILURE;
+ }
+
+ uint32_t type = (((uint32_t *)geometry_start)[0]);
+ /* Setup double_array_start depending on the geometry type */
+ double *double_array_start = NULL;
+ switch (type)
+ {
+ case (POINTTYPE):
+ /* For points we only need to jump over the type and npoints 32b ints */
+ double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t));
+ break;
+
+ default:
+ lwerror("%s is currently not implemented for type %d", __func__, type);
+ return LW_FAILURE;
+ }
+
+ gserialized2_copy_point(double_array_start, g->gflags, out_point);
+ return LW_SUCCESS;
+}
+
/**
* Read the bounding box off a serialization and calculate one if
* it is not already there.
Modified: trunk/liblwgeom/gserialized2.h
===================================================================
--- trunk/liblwgeom/gserialized2.h 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/gserialized2.h 2019-07-12 16:03:58 UTC (rev 17593)
@@ -172,3 +172,4 @@
int gserialized2_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox);
+int gserialized2_peek_first_point(const GSERIALIZED *g, POINT4D *out_point);
Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/liblwgeom/liblwgeom.h.in 2019-07-12 16:03:58 UTC (rev 17593)
@@ -839,6 +839,11 @@
*/
extern uint32_t gserialized_get_version(const GSERIALIZED *g);
+/**
+* Pull the first point values of a #GSERIALIZED. Only works for POINTTYPE
+*/
+extern int gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point);
+
/*****************************************************************************/
Modified: trunk/postgis/lwgeom_ogc.c
===================================================================
--- trunk/postgis/lwgeom_ogc.c 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/postgis/lwgeom_ogc.c 2019-07-12 16:03:58 UTC (rev 17593)
@@ -599,26 +599,17 @@
PG_FUNCTION_INFO_V1(LWGEOM_x_point);
Datum LWGEOM_x_point(PG_FUNCTION_ARGS)
{
- GSERIALIZED *geom;
- LWGEOM *lwgeom;
- LWPOINT *point = NULL;
- POINT2D p;
+ GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+ POINT4D pt;
- geom = PG_GETARG_GSERIALIZED_P(0);
+ if (gserialized_get_type(geom) != POINTTYPE)
+ lwpgerror("Argument to ST_X() must have type POINT");
- if ( gserialized_get_type(geom) != POINTTYPE )
- lwpgerror("Argument to ST_X() must be a point");
-
- lwgeom = lwgeom_from_gserialized(geom);
- point = lwgeom_as_lwpoint(lwgeom);
-
- if ( lwgeom_is_empty(lwgeom) )
+ if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
+ {
PG_RETURN_NULL();
-
- getPoint2d_p(point->point, 0, &p);
-
- PG_FREE_IF_COPY(geom, 0);
- PG_RETURN_FLOAT8(p.x);
+ }
+ PG_RETURN_FLOAT8(pt.x);
}
/**
@@ -628,27 +619,17 @@
PG_FUNCTION_INFO_V1(LWGEOM_y_point);
Datum LWGEOM_y_point(PG_FUNCTION_ARGS)
{
- GSERIALIZED *geom;
- LWPOINT *point = NULL;
- LWGEOM *lwgeom;
- POINT2D p;
+ GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+ POINT4D pt;
- geom = PG_GETARG_GSERIALIZED_P(0);
+ if (gserialized_get_type(geom) != POINTTYPE)
+ lwpgerror("Argument to ST_Y() must have type POINT");
- if ( gserialized_get_type(geom) != POINTTYPE )
- lwpgerror("Argument to ST_Y() must be a point");
-
- lwgeom = lwgeom_from_gserialized(geom);
- point = lwgeom_as_lwpoint(lwgeom);
-
- if ( lwgeom_is_empty(lwgeom) )
+ if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
+ {
PG_RETURN_NULL();
-
- getPoint2d_p(point->point, 0, &p);
-
- PG_FREE_IF_COPY(geom, 0);
-
- PG_RETURN_FLOAT8(p.y);
+ }
+ PG_RETURN_FLOAT8(pt.y);
}
/**
@@ -659,30 +640,17 @@
PG_FUNCTION_INFO_V1(LWGEOM_z_point);
Datum LWGEOM_z_point(PG_FUNCTION_ARGS)
{
- GSERIALIZED *geom;
- LWPOINT *point = NULL;
- LWGEOM *lwgeom;
- POINT3DZ p;
+ GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+ POINT4D pt;
- geom = PG_GETARG_GSERIALIZED_P(0);
+ if (gserialized_get_type(geom) != POINTTYPE)
+ lwpgerror("Argument to ST_Z() must have type POINT");
- if ( gserialized_get_type(geom) != POINTTYPE )
- lwpgerror("Argument to ST_Z() must be a point");
-
- lwgeom = lwgeom_from_gserialized(geom);
- point = lwgeom_as_lwpoint(lwgeom);
-
- if ( lwgeom_is_empty(lwgeom) )
+ if (!gserialized_has_z(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
+ {
PG_RETURN_NULL();
-
- /* no Z in input */
- if ( ! gserialized_has_z(geom) ) PG_RETURN_NULL();
-
- getPoint3dz_p(point->point, 0, &p);
-
- PG_FREE_IF_COPY(geom, 0);
-
- PG_RETURN_FLOAT8(p.z);
+ }
+ PG_RETURN_FLOAT8(pt.z);
}
/** M(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its M value.
@@ -692,27 +660,17 @@
PG_FUNCTION_INFO_V1(LWGEOM_m_point);
Datum LWGEOM_m_point(PG_FUNCTION_ARGS)
{
- GSERIALIZED *geom;
- LWPOINT *point = NULL;
- LWGEOM *lwgeom;
- POINT3DM p;
+ GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+ POINT4D pt;
- geom = PG_GETARG_GSERIALIZED_P(0);
+ if (gserialized_get_type(geom) != POINTTYPE)
+ lwpgerror("Argument to ST_M() must have type POINT");
- if ( gserialized_get_type(geom) != POINTTYPE )
- lwpgerror("Argument to ST_M() must be a point");
-
- lwgeom = lwgeom_from_gserialized(geom);
- point = lwgeom_as_lwpoint(lwgeom);
-
- if (lwgeom_is_empty(lwgeom) || !lwgeom_has_m(lwgeom))
+ if (!gserialized_has_m(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
+ {
PG_RETURN_NULL();
-
- getPoint3dm_p(point->point, 0, &p);
-
- PG_FREE_IF_COPY(geom, 0);
-
- PG_RETURN_FLOAT8(p.m);
+ }
+ PG_RETURN_FLOAT8(pt.m);
}
/**
Modified: trunk/regress/core/Makefile.in
===================================================================
--- trunk/regress/core/Makefile.in 2019-07-12 16:00:06 UTC (rev 17592)
+++ trunk/regress/core/Makefile.in 2019-07-12 16:03:58 UTC (rev 17593)
@@ -161,7 +161,8 @@
regress_brin_index_3d \
regress_brin_index_geography \
minimum_clearance \
- oriented_envelope
+ oriented_envelope \
+ point_coordinates
# Slow slow tests
TESTS_SLOW = \
Added: trunk/regress/core/point_coordinates.sql
===================================================================
--- trunk/regress/core/point_coordinates.sql (rev 0)
+++ trunk/regress/core/point_coordinates.sql 2019-07-12 16:03:58 UTC (rev 17593)
@@ -0,0 +1,35 @@
+SELECT 'X1', ST_X('POINT (0 0)'::geometry);
+SELECT 'X2', ST_X('POINTZ (1 2 3)'::geometry);
+SELECT 'X3', ST_X('POINTM (6 7 8)'::geometry);
+SELECT 'X4', ST_X('POINTZM (10 11 12 13)'::geometry);
+SELECT 'X5', ST_X('MULTIPOINT ((0 0), (1 1))'::geometry);
+SELECT 'X6', ST_X('LINESTRING (0 0, 1 1)'::geometry);
+SELECT 'X7', ST_X('GEOMETRYCOLLECTION (POINT(0 0))'::geometry);
+SELECT 'X8', ST_X('GEOMETRYCOLLECTION (POINT(0 1), LINESTRING(0 0, 1 1))'::geometry);
+
+SELECT 'Y1', ST_Y('POINT (0 0)'::geometry);
+SELECT 'Y2', ST_Y('POINTZ (1 2 3)'::geometry);
+SELECT 'Y3', ST_Y('POINTM (6 7 8)'::geometry);
+SELECT 'Y4', ST_Y('POINTZM (10 11 12 13)'::geometry);
+SELECT 'Y5', ST_Y('MULTIPOINT ((0 0), (1 1))'::geometry);
+SELECT 'Y6', ST_Y('LINESTRING (0 0, 1 1)'::geometry);
+SELECT 'Y7', ST_Y('GEOMETRYCOLLECTION (POINT(0 0))'::geometry);
+SELECT 'Y8', ST_Y('GEOMETRYCOLLECTION (POINT(0 1), LINESTRING(0 0, 1 1))'::geometry);
+
+SELECT 'Z1', ST_Z('POINT (0 0)'::geometry);
+SELECT 'Z2', ST_Z('POINTZ (1 2 3)'::geometry);
+SELECT 'Z3', ST_Z('POINTM (6 7 8)'::geometry);
+SELECT 'Z4', ST_Z('POINTZM (10 11 12 13)'::geometry);
+SELECT 'Z5', ST_Z('MULTIPOINT ((0 0), (1 1))'::geometry);
+SELECT 'Z6', ST_Z('LINESTRING (0 0, 1 1)'::geometry);
+SELECT 'Z7', ST_Z('GEOMETRYCOLLECTION (POINT(0 0))'::geometry);
+SELECT 'Z8', ST_Z('GEOMETRYCOLLECTION (POINT(0 1), LINESTRING(0 0, 1 1))'::geometry);
+
+SELECT 'M1', ST_M('POINT (0 0)'::geometry);
+SELECT 'M2', ST_M('POINTZ (1 2 3)'::geometry);
+SELECT 'M3', ST_M('POINTM (6 7 8)'::geometry);
+SELECT 'M4', ST_M('POINTZM (10 11 12 13)'::geometry);
+SELECT 'M5', ST_M('MULTIPOINT ((0 0), (1 1))'::geometry);
+SELECT 'M6', ST_M('LINESTRING (0 0, 1 1)'::geometry);
+SELECT 'M7', ST_M('GEOMETRYCOLLECTION (POINT(0 0))'::geometry);
+SELECT 'M8', ST_M('GEOMETRYCOLLECTION (POINT(0 1), LINESTRING(0 0, 1 1))'::geometry);
Added: trunk/regress/core/point_coordinates_expected
===================================================================
--- trunk/regress/core/point_coordinates_expected (rev 0)
+++ trunk/regress/core/point_coordinates_expected 2019-07-12 16:03:58 UTC (rev 17593)
@@ -0,0 +1,32 @@
+X1|0
+X2|1
+X3|6
+X4|10
+ERROR: Argument to ST_X() must have type POINT
+ERROR: Argument to ST_X() must have type POINT
+ERROR: Argument to ST_X() must have type POINT
+ERROR: Argument to ST_X() must have type POINT
+Y1|0
+Y2|2
+Y3|7
+Y4|11
+ERROR: Argument to ST_Y() must have type POINT
+ERROR: Argument to ST_Y() must have type POINT
+ERROR: Argument to ST_Y() must have type POINT
+ERROR: Argument to ST_Y() must have type POINT
+Z1|
+Z2|3
+Z3|
+Z4|12
+ERROR: Argument to ST_Z() must have type POINT
+ERROR: Argument to ST_Z() must have type POINT
+ERROR: Argument to ST_Z() must have type POINT
+ERROR: Argument to ST_Z() must have type POINT
+M1|
+M2|
+M3|8
+M4|13
+ERROR: Argument to ST_M() must have type POINT
+ERROR: Argument to ST_M() must have type POINT
+ERROR: Argument to ST_M() must have type POINT
+ERROR: Argument to ST_M() must have type POINT
More information about the postgis-tickets
mailing list