[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