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