PostGIS  3.4.0dev-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 cstring_to_text */
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_transform.h" /* for srid_is_latlon */
46 
47 
48 Datum geography_typmod_in(PG_FUNCTION_ARGS);
49 Datum geometry_typmod_in(PG_FUNCTION_ARGS);
50 Datum postgis_typmod_out(PG_FUNCTION_ARGS);
51 Datum postgis_typmod_dims(PG_FUNCTION_ARGS);
52 Datum postgis_typmod_srid(PG_FUNCTION_ARGS);
53 Datum postgis_typmod_type(PG_FUNCTION_ARGS);
54 Datum geography_enforce_typmod(PG_FUNCTION_ARGS);
55 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS);
56 
57 
58 /*
59 ** postgis_typmod_out(int) returns cstring
60 */
62 Datum postgis_typmod_out(PG_FUNCTION_ARGS)
63 {
64  StringInfoData si;
65  // char *s = (char*)palloc(64);
66  int32 typmod = PG_GETARG_INT32(0);
67  int32 srid = TYPMOD_GET_SRID(typmod);
68  int32 type = TYPMOD_GET_TYPE(typmod);
69  int32 hasz = TYPMOD_GET_Z(typmod);
70  int32 hasm = TYPMOD_GET_M(typmod);
71 
72  POSTGIS_DEBUGF(3, "Got typmod(srid = %d, type = %d, hasz = %d, hasm = %d)", srid, type, hasz, hasm);
73 
74  /* No SRID or type or dimensionality? Then no typmod at all. Return empty string. */
75  if (!(srid || type || hasz || hasm) || typmod < 0)
76  {
77  PG_RETURN_CSTRING(pstrdup(""));
78  }
79 
80  /* Opening bracket. */
81  initStringInfo(&si);
82  appendStringInfoChar(&si, '(');
83 
84  /* Has type? */
85  if (type)
86  appendStringInfo(&si, "%s", lwtype_name(type));
87  else if (srid || hasz || hasm)
88  appendStringInfoString(&si, "Geometry");
89 
90  /* Has Z? */
91  if (hasz) appendStringInfoString(&si, "Z");
92 
93  /* Has M? */
94  if (hasm) appendStringInfoString(&si, "M");
95 
96  /* Has SRID? */
97  if (srid) appendStringInfo(&si, ",%d", srid);
98 
99  /* Closing bracket. */
100  appendStringInfoChar(&si, ')');
101 
102  PG_RETURN_CSTRING(si.data);
103 }
104 
105 
111 {
112  int32 geom_srid = gserialized_get_srid(gser);
113  int32 geom_type = gserialized_get_type(gser);
114  int32 geom_z = gserialized_has_z(gser);
115  int32 geom_m = gserialized_has_m(gser);
116  int32 typmod_srid = TYPMOD_GET_SRID(typmod);
117  int32 typmod_type = TYPMOD_GET_TYPE(typmod);
118  int32 typmod_z = TYPMOD_GET_Z(typmod);
119  int32 typmod_m = TYPMOD_GET_M(typmod);
120 
121  POSTGIS_DEBUG(2, "Entered function");
122 
123  /* No typmod (-1) => no preferences */
124  if (typmod < 0) return gser;
125 
126  POSTGIS_DEBUGF(3, "Got geom(type = %d, srid = %d, hasz = %d, hasm = %d)", geom_type, geom_srid, geom_z, geom_m);
127  POSTGIS_DEBUGF(3, "Got typmod(type = %d, srid = %d, hasz = %d, hasm = %d)", typmod_type, typmod_srid, typmod_z, typmod_m);
128 
129  /*
130  * #3031: If a user is handing us a MULTIPOINT EMPTY but trying to fit it into
131  * a POINT geometry column, there's a strong chance the reason she has
132  * a MULTIPOINT EMPTY because we gave it to her during data dump,
133  * converting the internal POINT EMPTY into a EWKB MULTIPOINT EMPTY
134  * (because EWKB doesn't have a clean way to represent POINT EMPTY).
135  * In such a case, it makes sense to turn the MULTIPOINT EMPTY back into a
136  * point EMPTY, rather than throwing an error.
137  */
138  if ( typmod_type == POINTTYPE && geom_type == MULTIPOINTTYPE &&
139  gserialized_is_empty(gser) )
140  {
141  LWPOINT *empty_point = lwpoint_construct_empty(geom_srid, geom_z, geom_m);
142  geom_type = POINTTYPE;
143  pfree(gser);
144  if ( gserialized_is_geodetic(gser) )
145  gser = geography_serialize(lwpoint_as_lwgeom(empty_point));
146  else
147  gser = geometry_serialize(lwpoint_as_lwgeom(empty_point));
148  }
149 
150  /* Typmod has a preference for SRID, but geometry does not? Harmonize the geometry SRID. */
151  if ( typmod_srid > 0 && geom_srid == 0 )
152  {
153  gserialized_set_srid(gser, typmod_srid);
154  geom_srid = typmod_srid;
155  }
156 
157  /* Typmod has a preference for SRID? Geometry SRID had better match. */
158  if ( typmod_srid > 0 && typmod_srid != geom_srid )
159  {
160  ereport(ERROR, (
161  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
162  errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) ));
163  }
164 
165  /* Typmod has a preference for MULTI* geometry type */
166  /* and geometry is the singleton type. */
167  if ( typmod_type > 0 && typmod_type == lwtype_multitype(geom_type) )
168  {
169  /* Promote the singleton to equivalent multi */
170  LWGEOM *geom = lwgeom_from_gserialized(gser);
171  LWGEOM *mgeom = lwgeom_as_multi(geom);
172  GSERIALIZED *mgser = gserialized_is_geodetic(gser) ?
173  geography_serialize(mgeom) :
174  geometry_serialize(mgeom);
175  /* Count on caller memory context cleaning up dangling gserialized */
176  gser = mgser;
177  geom_type = gserialized_get_type(gser);
178  lwgeom_free(geom);
179  lwgeom_free(mgeom);
180  }
181 
182  /* Typmod has a preference for geometry type. */
183  if ( typmod_type > 0 &&
184  /* GEOMETRYCOLLECTION column can hold any kind of collection */
185  ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE ||
186  geom_type == MULTIPOLYGONTYPE ||
187  geom_type == MULTIPOINTTYPE ||
188  geom_type == MULTILINETYPE )) ||
189  /* Other types must be strictly equal. */
190  (typmod_type != geom_type)) )
191  {
192  ereport(ERROR, (
193  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
194  errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) ));
195  }
196 
197  /* Mismatched Z dimensionality. */
198  if ( typmod_z && ! geom_z )
199  {
200  ereport(ERROR, (
201  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
202  errmsg("Column has Z dimension but geometry does not" )));
203  }
204 
205  /* Mismatched Z dimensionality (other way). */
206  if ( geom_z && ! typmod_z )
207  {
208  ereport(ERROR, (
209  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
210  errmsg("Geometry has Z dimension but column does not" )));
211  }
212 
213  /* Mismatched M dimensionality. */
214  if ( typmod_m && ! geom_m )
215  {
216  ereport(ERROR, (
217  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
218  errmsg("Column has M dimension but geometry does not" )));
219  }
220 
221  /* Mismatched M dimensionality (other way). */
222  if ( geom_m && ! typmod_m )
223  {
224  ereport(ERROR, (
225  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
226  errmsg("Geometry has M dimension but column does not" )));
227  }
228 
229  return gser;
230 
231 }
232 
233 
234 static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
235 {
236  int32 typmod = 0;
237  Datum *elem_values;
238  int n = 0;
239  int i = 0;
240 
241  if (ARR_ELEMTYPE(arr) != CSTRINGOID)
242  ereport(ERROR,
243  (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
244  errmsg("typmod array must be type cstring[]")));
245 
246  if (ARR_NDIM(arr) != 1)
247  ereport(ERROR,
248  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
249  errmsg("typmod array must be one-dimensional")));
250 
251  if (ARR_HASNULL(arr))
252  ereport(ERROR,
253  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
254  errmsg("typmod array must not contain nulls")));
255 
256  deconstruct_array(arr,
257  CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */
258  &elem_values, NULL, &n);
259 
260  /* Set the SRID to the default value first */
261  if (is_geography)
262  TYPMOD_SET_SRID(typmod, SRID_DEFAULT);
263  else
264  TYPMOD_SET_SRID(typmod, SRID_UNKNOWN);
265 
266  for (i = 0; i < n; i++)
267  {
268  if ( i == 0 ) /* TYPE */
269  {
270  char *s = DatumGetCString(elem_values[i]);
271  uint8_t type = 0;
272  int z = 0;
273  int m = 0;
274 
275  if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE )
276  {
277  ereport(ERROR,
278  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
279  errmsg("Invalid geometry type modifier: %s", s)));
280  }
281  else
282  {
283  TYPMOD_SET_TYPE(typmod, type);
284  if ( z )
285  TYPMOD_SET_Z(typmod);
286  if ( m )
287  TYPMOD_SET_M(typmod);
288  }
289  }
290  if ( i == 1 ) /* SRID */
291  {
292  char *int_string = DatumGetCString(elem_values[i]);
293  char *endp;
294  long l;
295  int32_t srid;
296 
297  errno = 0;
298  l = strtol(int_string, &endp, 10);
299 
300  if (int_string == endp)
301  ereport(ERROR,
302  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
303  errmsg("invalid input syntax for type %s: \"%s\"",
304  "integer", int_string)));
305 
306  if (errno == ERANGE || l < INT_MIN || l > INT_MAX)
307  ereport(ERROR,
308  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
309  errmsg("value \"%s\" is out of range for type %s", int_string,
310  "integer")));
311 
312  if (*endp != '\0')
313  ereport(ERROR,
314  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
315  errmsg("invalid input syntax for type %s: \"%s\"",
316  "integer", int_string)));
317 
318  srid = clamp_srid(l);
319  POSTGIS_DEBUGF(3, "srid: %d", srid);
320  if ( srid != SRID_UNKNOWN )
321  {
322  TYPMOD_SET_SRID(typmod, srid);
323  }
324  }
325  }
326 
327  pfree(elem_values);
328 
329  return typmod;
330 }
331 
332 /*
333 ** geography_typmod_in(cstring[]) returns int32
334 **
335 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
336 */
338 Datum geography_typmod_in(PG_FUNCTION_ARGS)
339 {
340  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
341  int32 typmod = gserialized_typmod_in(arr, LW_TRUE);
342  int32_t srid = TYPMOD_GET_SRID(typmod);
343  /* Check the SRID is legal (geographic coordinates) */
344  srid_check_latlong(srid);
345 
346  PG_RETURN_INT32(typmod);
347 }
348 
349 /*
350 ** geometry_typmod_in(cstring[]) returns int32
351 **
352 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
353 */
355 Datum geometry_typmod_in(PG_FUNCTION_ARGS)
356 {
357  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
358  uint32 typmod = gserialized_typmod_in(arr, LW_FALSE); /* Not a geography */;
359  PG_RETURN_INT32(typmod);
360 }
361 
362 /*
363 ** geography_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
364 ** Ensure that an incoming geometry conforms to typmod restrictions on
365 ** type, dims and srid.
366 */
368 Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
369 {
370  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
371  int32 typmod = PG_GETARG_INT32(1);
372  /* We don't need to have different behavior based on explicitness. */
373  /* bool isExplicit = PG_GETARG_BOOL(2); */
374 
375  /* Check if geometry typmod is consistent with the supplied one. */
376  arg = postgis_valid_typmod(arg, typmod);
377 
378  PG_RETURN_POINTER(arg);
379 }
380 
381 /*
382 ** geometry_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
383 ** Ensure that an incoming geometry conforms to typmod restrictions on
384 ** type, dims and srid.
385 */
387 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
388 {
389  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
390  int32 typmod = PG_GETARG_INT32(1);
391  /* We don't need to have different behavior based on explicitness. */
392  /* bool isExplicit = PG_GETARG_BOOL(2); */
393 
394  /* Check if geometry typmod is consistent with the supplied one. */
395  arg = postgis_valid_typmod(arg, typmod);
396 
397  PG_RETURN_POINTER(arg);
398 }
399 
400 
401 /*
402 ** postgis_typmod_type(uint32) returns cstring
403 ** Used for geometry_columns and other views on system tables
404 */
406 Datum postgis_typmod_type(PG_FUNCTION_ARGS)
407 {
408  int32 typmod = PG_GETARG_INT32(0);
409  int32 type = TYPMOD_GET_TYPE(typmod);
410  char *s = (char*)palloc(64);
411  char *ptr = s;
412  text *stext;
413 
414  /* Has type? */
415  if ( typmod < 0 || type == 0 )
416  ptr += sprintf(ptr, "Geometry");
417  else
418  ptr += sprintf(ptr, "%s", lwtype_name(type));
419 
420  /* Has Z? */
421  if ( typmod >= 0 && TYPMOD_GET_Z(typmod) )
422  ptr += sprintf(ptr, "%s", "Z");
423 
424  /* Has M? */
425  if ( typmod >= 0 && TYPMOD_GET_M(typmod) )
426  ptr += sprintf(ptr, "%s", "M");
427 
428  stext = cstring_to_text(s);
429  pfree(s);
430  PG_RETURN_TEXT_P(stext);
431 }
432 
433 /*
434 ** postgis_typmod_dims(uint32) returns int
435 ** Used for geometry_columns and other views on system tables
436 */
438 Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
439 {
440  int32 typmod = PG_GETARG_INT32(0);
441  int32 dims = 2;
442  if ( typmod < 0 )
443  PG_RETURN_NULL(); /* unconstrained */
444  if ( TYPMOD_GET_Z(typmod) )
445  dims++;
446  if ( TYPMOD_GET_M(typmod) )
447  dims++;
448  PG_RETURN_INT32(dims);
449 }
450 
451 /*
452 ** postgis_typmod_srid(uint32) returns int
453 ** Used for geometry_columns and other views on system tables
454 */
456 Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
457 {
458  int32 typmod = PG_GETARG_INT32(0);
459  if ( typmod < 0 )
460  PG_RETURN_INT32(0);
461  PG_RETURN_INT32(TYPMOD_GET_SRID(typmod));
462 }
463 
char * s
Definition: cu_in_wkt.c:23
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:126
int gserialized_is_geodetic(const GSERIALIZED *g)
Check if a GSERIALIZED is a geography.
Definition: gserialized.c:196
int gserialized_has_m(const GSERIALIZED *g)
Check if a GSERIALIZED has an M ordinate.
Definition: gserialized.c:185
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:152
void gserialized_set_srid(GSERIALIZED *g, int32_t srid)
Write the SRID into the serialized form (it is packed into three bytes so this is a handy function).
Definition: gserialized.c:138
int gserialized_has_z(const GSERIALIZED *g)
Check if a GSERIALIZED has a Z ordinate.
Definition: gserialized.c:174
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
Datum postgis_typmod_type(PG_FUNCTION_ARGS)
Datum geography_typmod_in(PG_FUNCTION_ARGS)
Datum geometry_typmod_in(PG_FUNCTION_ARGS)
Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
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...
static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
Datum postgis_typmod_out(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(postgis_typmod_out)
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
#define TYPMOD_GET_SRID(typmod)
Macros for manipulating the 'typemod' int.
Definition: liblwgeom.h:192
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
#define TYPMOD_SET_SRID(typmod, srid)
Definition: liblwgeom.h:193
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define MULTILINETYPE
Definition: liblwgeom.h:106
LWGEOM * lwgeom_as_multi(const LWGEOM *lwgeom)
Create a new LWGEOM of the appropriate MULTI* type.
Definition: lwgeom.c:380
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m)
Utility function to get type number from string.
Definition: lwutil.c:489
#define TYPMOD_GET_M(typmod)
Definition: liblwgeom.h:198
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
#define TYPMOD_SET_TYPE(typmod, type)
Definition: liblwgeom.h:195
#define SRID_DEFAULT
Definition: liblwgeom.h:225
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
uint8_t lwtype_multitype(uint8_t type)
Definition: lwgeom.c:370
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define TYPMOD_GET_TYPE(typmod)
Definition: liblwgeom.h:194
#define TYPMOD_SET_M(typmod)
Definition: liblwgeom.h:199
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
#define TYPMOD_SET_Z(typmod)
Definition: liblwgeom.h:197
#define TYPMOD_GET_Z(typmod)
Definition: liblwgeom.h:196
int32_t clamp_srid(int32_t srid)
Return a valid SRID from an arbitrary integer Raises a notice if what comes out is different from wha...
Definition: lwutil.c:333
This library is the generic geometry handling section of PostGIS.
type
Definition: ovdump.py:42
unsigned int int32
Definition: shpopen.c:54