PostGIS  2.4.9dev-r@@SVN_REVISION@@
gserialized_typmod.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22  *
23  **********************************************************************/
24 
25 
26 #include "postgres.h"
27 
28 #include "../postgis_config.h"
29 
30 #include <math.h>
31 #include <float.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <errno.h>
35 
36 #include "utils/elog.h"
37 #include "utils/array.h"
38 #include "utils/builtins.h" /* for pg_atoi */
39 #include "lib/stringinfo.h" /* For binary input */
40 #include "catalog/pg_type.h" /* for CSTRINGOID */
41 
42 #include "liblwgeom.h" /* For standard geometry types. */
43 #include "lwgeom_pg.h" /* For debugging macros. */
44 #include "geography.h" /* For utility functions. */
45 #include "lwgeom_export.h" /* For export functions. */
46 #include "lwgeom_transform.h" /* for srid_is_latlon */
47 
48 
49 Datum geography_typmod_in(PG_FUNCTION_ARGS);
50 Datum geometry_typmod_in(PG_FUNCTION_ARGS);
51 Datum postgis_typmod_out(PG_FUNCTION_ARGS);
52 Datum postgis_typmod_dims(PG_FUNCTION_ARGS);
53 Datum postgis_typmod_srid(PG_FUNCTION_ARGS);
54 Datum postgis_typmod_type(PG_FUNCTION_ARGS);
55 Datum geography_enforce_typmod(PG_FUNCTION_ARGS);
56 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS);
57 
58 
59 /*
60 ** postgis_typmod_out(int) returns cstring
61 */
63 Datum postgis_typmod_out(PG_FUNCTION_ARGS)
64 {
65  char *s = (char*)palloc(64);
66  char *str = s;
67  uint32 typmod = PG_GETARG_INT32(0);
68  uint32 srid = TYPMOD_GET_SRID(typmod);
69  uint32 type = TYPMOD_GET_TYPE(typmod);
70  uint32 hasz = TYPMOD_GET_Z(typmod);
71  uint32 hasm = TYPMOD_GET_M(typmod);
72 
73  POSTGIS_DEBUGF(3, "Got typmod(srid = %d, type = %d, hasz = %d, hasm = %d)", srid, type, hasz, hasm);
74 
75  /* No SRID or type or dimensionality? Then no typmod at all. Return empty string. */
76  if ( ! ( srid || type || hasz || hasm ) )
77  {
78  *str = '\0';
79  PG_RETURN_CSTRING(str);
80  }
81 
82  /* Opening bracket. */
83  str += sprintf(str, "(");
84 
85  /* Has type? */
86  if ( type )
87  str += sprintf(str, "%s", lwtype_name(type));
88  else if ( (!type) && ( srid || hasz || hasm ) )
89  str += sprintf(str, "Geometry");
90 
91  /* Has Z? */
92  if ( hasz )
93  str += sprintf(str, "%s", "Z");
94 
95  /* Has M? */
96  if ( hasm )
97  str += sprintf(str, "%s", "M");
98 
99  /* Comma? */
100  if ( srid )
101  str += sprintf(str, ",");
102 
103  /* Has SRID? */
104  if ( srid )
105  str += sprintf(str, "%d", srid);
106 
107  /* Closing bracket. */
108  str += sprintf(str, ")");
109 
110  PG_RETURN_CSTRING(s);
111 
112 }
113 
114 
120 {
121  int32 geom_srid = gserialized_get_srid(gser);
122  int32 geom_type = gserialized_get_type(gser);
123  int32 geom_z = gserialized_has_z(gser);
124  int32 geom_m = gserialized_has_m(gser);
125  int32 typmod_srid = TYPMOD_GET_SRID(typmod);
126  int32 typmod_type = TYPMOD_GET_TYPE(typmod);
127  int32 typmod_z = TYPMOD_GET_Z(typmod);
128  int32 typmod_m = TYPMOD_GET_M(typmod);
129 
130  POSTGIS_DEBUG(2, "Entered function");
131 
132  /* No typmod (-1) => no preferences */
133  if (typmod < 0) return gser;
134 
135  POSTGIS_DEBUGF(3, "Got geom(type = %d, srid = %d, hasz = %d, hasm = %d)", geom_type, geom_srid, geom_z, geom_m);
136  POSTGIS_DEBUGF(3, "Got typmod(type = %d, srid = %d, hasz = %d, hasm = %d)", typmod_type, typmod_srid, typmod_z, typmod_m);
137 
138  /*
139  * #3031: If a user is handing us a MULTIPOINT EMPTY but trying to fit it into
140  * a POINT geometry column, there's a strong chance the reason she has
141  * a MULTIPOINT EMPTY because we gave it to her during data dump,
142  * converting the internal POINT EMPTY into a EWKB MULTIPOINT EMPTY
143  * (because EWKB doesn't have a clean way to represent POINT EMPTY).
144  * In such a case, it makes sense to turn the MULTIPOINT EMPTY back into a
145  * point EMPTY, rather than throwing an error.
146  */
147  if ( typmod_type == POINTTYPE && geom_type == MULTIPOINTTYPE &&
148  gserialized_is_empty(gser) )
149  {
150  LWPOINT *empty_point = lwpoint_construct_empty(geom_srid, geom_z, geom_m);
151  geom_type = POINTTYPE;
152  pfree(gser);
153  if ( gserialized_is_geodetic(gser) )
154  gser = geography_serialize(lwpoint_as_lwgeom(empty_point));
155  else
156  gser = geometry_serialize(lwpoint_as_lwgeom(empty_point));
157  }
158 
159  /* Typmod has a preference for SRID? Geometry SRID had better match. */
160  if ( typmod_srid > 0 && typmod_srid != geom_srid )
161  {
162  ereport(ERROR, (
163  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
164  errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) ));
165  }
166 
167  /* Typmod has a preference for geometry type. */
168  if ( typmod_type > 0 &&
169  /* GEOMETRYCOLLECTION column can hold any kind of collection */
170  ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE ||
171  geom_type == MULTIPOLYGONTYPE ||
172  geom_type == MULTIPOINTTYPE ||
173  geom_type == MULTILINETYPE )) ||
174  /* Other types must be strictly equal. */
175  (typmod_type != geom_type)) )
176  {
177  ereport(ERROR, (
178  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179  errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) ));
180  }
181 
182  /* Mismatched Z dimensionality. */
183  if ( typmod_z && ! geom_z )
184  {
185  ereport(ERROR, (
186  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
187  errmsg("Column has Z dimension but geometry does not" )));
188  }
189 
190  /* Mismatched Z dimensionality (other way). */
191  if ( geom_z && ! typmod_z )
192  {
193  ereport(ERROR, (
194  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
195  errmsg("Geometry has Z dimension but column does not" )));
196  }
197 
198  /* Mismatched M dimensionality. */
199  if ( typmod_m && ! geom_m )
200  {
201  ereport(ERROR, (
202  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
203  errmsg("Column has M dimension but geometry does not" )));
204  }
205 
206  /* Mismatched M dimensionality (other way). */
207  if ( geom_m && ! typmod_m )
208  {
209  ereport(ERROR, (
210  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
211  errmsg("Geometry has M dimension but column does not" )));
212  }
213 
214  return gser;
215 
216 }
217 
218 
219 static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
220 {
221  uint32 typmod = 0;
222  Datum *elem_values;
223  int n = 0;
224  int i = 0;
225 
226  if (ARR_ELEMTYPE(arr) != CSTRINGOID)
227  ereport(ERROR,
228  (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
229  errmsg("typmod array must be type cstring[]")));
230 
231  if (ARR_NDIM(arr) != 1)
232  ereport(ERROR,
233  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
234  errmsg("typmod array must be one-dimensional")));
235 
236  if (ARR_HASNULL(arr))
237  ereport(ERROR,
238  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
239  errmsg("typmod array must not contain nulls")));
240 
241  deconstruct_array(arr,
242  CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */
243  &elem_values, NULL, &n);
244 
245  /* Set the SRID to the default value first */
246  if ( is_geography)
247  TYPMOD_SET_SRID(typmod, SRID_DEFAULT);
248  else
249  TYPMOD_SET_SRID(typmod, SRID_UNKNOWN);
250 
251  for (i = 0; i < n; i++)
252  {
253  if ( i == 0 ) /* TYPE */
254  {
255  char *s = DatumGetCString(elem_values[i]);
256  uint8_t type = 0;
257  int z = 0;
258  int m = 0;
259 
260  if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE )
261  {
262  ereport(ERROR,
263  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
264  errmsg("Invalid geometry type modifier: %s", s)));
265  }
266  else
267  {
268  TYPMOD_SET_TYPE(typmod, type);
269  if ( z )
270  TYPMOD_SET_Z(typmod);
271  if ( m )
272  TYPMOD_SET_M(typmod);
273  }
274  }
275  if ( i == 1 ) /* SRID */
276  {
277  int srid = pg_atoi(DatumGetCString(elem_values[i]),
278  sizeof(int32), '\0');
279  srid = clamp_srid(srid);
280  POSTGIS_DEBUGF(3, "srid: %d", srid);
281  if ( srid != SRID_UNKNOWN )
282  {
283  TYPMOD_SET_SRID(typmod, srid);
284  }
285  }
286  }
287 
288  pfree(elem_values);
289 
290  return typmod;
291 }
292 
293 /*
294 ** geography_typmod_in(cstring[]) returns int32
295 **
296 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
297 */
299 Datum geography_typmod_in(PG_FUNCTION_ARGS)
300 {
301  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
302  uint32 typmod = gserialized_typmod_in(arr, LW_TRUE);
303  int srid = TYPMOD_GET_SRID(typmod);
304  /* Check the SRID is legal (geographic coordinates) */
305  srid_is_latlong(fcinfo, srid);
306 
307  PG_RETURN_INT32(typmod);
308 }
309 
310 /*
311 ** geometry_typmod_in(cstring[]) returns int32
312 **
313 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
314 */
316 Datum geometry_typmod_in(PG_FUNCTION_ARGS)
317 {
318  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
319  uint32 typmod = gserialized_typmod_in(arr, LW_FALSE); /* Not a geography */;
320  PG_RETURN_INT32(typmod);
321 }
322 
323 /*
324 ** geography_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
325 ** Ensure that an incoming geometry conforms to typmod restrictions on
326 ** type, dims and srid.
327 */
329 Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
330 {
331  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
332  int32 typmod = PG_GETARG_INT32(1);
333  /* We don't need to have different behavior based on explicitness. */
334  /* bool isExplicit = PG_GETARG_BOOL(2); */
335 
336  /* Check if geometry typmod is consistent with the supplied one. */
337  arg = postgis_valid_typmod(arg, typmod);
338 
339  PG_RETURN_POINTER(arg);
340 }
341 
342 /*
343 ** geometry_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
344 ** Ensure that an incoming geometry conforms to typmod restrictions on
345 ** type, dims and srid.
346 */
348 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
349 {
350  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
351  int32 typmod = PG_GETARG_INT32(1);
352  /* We don't need to have different behavior based on explicitness. */
353  /* bool isExplicit = PG_GETARG_BOOL(2); */
354 
355  /* Check if geometry typmod is consistent with the supplied one. */
356  arg = postgis_valid_typmod(arg, typmod);
357 
358  PG_RETURN_POINTER(arg);
359 }
360 
361 
362 /*
363 ** postgis_typmod_type(uint32) returns cstring
364 ** Used for geometry_columns and other views on system tables
365 */
367 Datum postgis_typmod_type(PG_FUNCTION_ARGS)
368 {
369  int32 typmod = PG_GETARG_INT32(0);
370  int32 type = TYPMOD_GET_TYPE(typmod);
371  char *s = (char*)palloc(64);
372  char *ptr = s;
373  text *stext;
374 
375  /* Has type? */
376  if ( typmod < 0 || type == 0 )
377  ptr += sprintf(ptr, "Geometry");
378  else
379  ptr += sprintf(ptr, "%s", lwtype_name(type));
380 
381  /* Has Z? */
382  if ( typmod >= 0 && TYPMOD_GET_Z(typmod) )
383  ptr += sprintf(ptr, "%s", "Z");
384 
385  /* Has M? */
386  if ( typmod >= 0 && TYPMOD_GET_M(typmod) )
387  ptr += sprintf(ptr, "%s", "M");
388 
389  stext = cstring2text(s);
390  pfree(s);
391  PG_RETURN_TEXT_P(stext);
392 }
393 
394 /*
395 ** postgis_typmod_dims(uint32) returns int
396 ** Used for geometry_columns and other views on system tables
397 */
399 Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
400 {
401  int32 typmod = PG_GETARG_INT32(0);
402  int32 dims = 2;
403  if ( typmod < 0 )
404  PG_RETURN_NULL(); /* unconstrained */
405  if ( TYPMOD_GET_Z(typmod) )
406  dims++;
407  if ( TYPMOD_GET_M(typmod) )
408  dims++;
409  PG_RETURN_INT32(dims);
410 }
411 
412 /*
413 ** postgis_typmod_srid(uint32) returns int
414 ** Used for geometry_columns and other views on system tables
415 */
417 Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
418 {
419  int32 typmod = PG_GETARG_INT32(0);
420  if ( typmod < 0 )
421  PG_RETURN_INT32(0);
422  PG_RETURN_INT32(TYPMOD_GET_SRID(typmod));
423 }
424 
#define TYPMOD_GET_TYPE(typmod)
Definition: liblwgeom.h:167
uint32_t gserialized_get_type(const GSERIALIZED *s)
Extract the geometry type from the serialized form (it hides in the anonymous data area...
Definition: g_serialized.c:86
Datum geometry_typmod_in(PG_FUNCTION_ARGS)
static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
int clamp_srid(int srid)
Return a valid SRID from an arbitrary integer Raises a notice if what comes out is different from wha...
Definition: lwutil.c:380
PG_FUNCTION_INFO_V1(postgis_typmod_out)
unsigned int int32
Definition: shpopen.c:273
#define TYPMOD_GET_M(typmod)
Definition: liblwgeom.h:171
#define TYPMOD_SET_Z(typmod)
Definition: liblwgeom.h:170
Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
int gserialized_has_m(const GSERIALIZED *gser)
Check if a GSERIALIZED has an M ordinate.
Definition: g_serialized.c:50
#define TYPMOD_GET_SRID(typmod)
Macros for manipulating the &#39;typemod&#39; int.
Definition: liblwgeom.h:165
#define MULTIPOINTTYPE
Definition: liblwgeom.h:88
int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m)
Calculate type integer and dimensional flags from string input.
Definition: g_util.c:163
#define SRID_DEFAULT
Definition: liblwgeom.h:195
#define TYPMOD_SET_M(typmod)
Definition: liblwgeom.h:172
Datum geography_typmod_in(PG_FUNCTION_ARGS)
LWPOINT * lwpoint_construct_empty(int srid, char hasz, char hasm)
Definition: lwpoint.c:151
Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
int gserialized_has_z(const GSERIALIZED *gser)
Check if a GSERIALIZED has a Z ordinate.
Definition: g_serialized.c:45
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: g_serialized.c:179
#define LW_FAILURE
Definition: liblwgeom.h:79
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:218
#define LW_FALSE
Definition: liblwgeom.h:77
GSERIALIZED * postgis_valid_typmod(GSERIALIZED *gser, int32_t typmod)
Check the consistency of the metadata we want to enforce in the typmod: srid, type and dimensionality...
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:188
char * s
Definition: cu_in_wkt.c:23
#define TYPMOD_SET_TYPE(typmod, type)
Definition: liblwgeom.h:168
Datum postgis_typmod_type(PG_FUNCTION_ARGS)
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:90
Datum postgis_typmod_out(PG_FUNCTION_ARGS)
Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
type
Definition: ovdump.py:41
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:303
int gserialized_is_geodetic(const GSERIALIZED *gser)
Check if a GSERIALIZED is a geography.
Definition: g_serialized.c:65
#define TYPMOD_SET_SRID(typmod, srid)
Definition: liblwgeom.h:166
#define TYPMOD_GET_Z(typmod)
Definition: liblwgeom.h:169
#define MULTILINETYPE
Definition: liblwgeom.h:89
unsigned char uint8_t
Definition: uthash.h:79
int32_t gserialized_get_srid(const GSERIALIZED *s)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: g_serialized.c:100
#define COLLECTIONTYPE
Definition: liblwgeom.h:91
This library is the generic geometry handling section of PostGIS.