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