PostGIS  2.5.0beta2dev-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  int32 typmod = PG_GETARG_INT32(0);
68  int32 srid = TYPMOD_GET_SRID(typmod);
69  int32 type = TYPMOD_GET_TYPE(typmod);
70  int32 hasz = TYPMOD_GET_Z(typmod);
71  int32 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) || typmod < 0)
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 (srid || hasz || hasm)
89  str += sprintf(str, "Geometry");
90 
91  /* Has Z? */
92  if (hasz) str += sprintf(str, "%s", "Z");
93 
94  /* Has M? */
95  if (hasm) str += sprintf(str, "%s", "M");
96 
97  /* Has SRID? */
98  if (srid) str += sprintf(str, ",%d", srid);
99 
100  /* Closing bracket. */
101  str += sprintf(str, ")");
102 
103  PG_RETURN_CSTRING(s);
104 
105 }
106 
107 
113 {
114  int32 geom_srid = gserialized_get_srid(gser);
115  int32 geom_type = gserialized_get_type(gser);
116  int32 geom_z = gserialized_has_z(gser);
117  int32 geom_m = gserialized_has_m(gser);
118  int32 typmod_srid = TYPMOD_GET_SRID(typmod);
119  int32 typmod_type = TYPMOD_GET_TYPE(typmod);
120  int32 typmod_z = TYPMOD_GET_Z(typmod);
121  int32 typmod_m = TYPMOD_GET_M(typmod);
122 
123  POSTGIS_DEBUG(2, "Entered function");
124 
125  /* No typmod (-1) => no preferences */
126  if (typmod < 0) return gser;
127 
128  POSTGIS_DEBUGF(3, "Got geom(type = %d, srid = %d, hasz = %d, hasm = %d)", geom_type, geom_srid, geom_z, geom_m);
129  POSTGIS_DEBUGF(3, "Got typmod(type = %d, srid = %d, hasz = %d, hasm = %d)", typmod_type, typmod_srid, typmod_z, typmod_m);
130 
131  /*
132  * #3031: If a user is handing us a MULTIPOINT EMPTY but trying to fit it into
133  * a POINT geometry column, there's a strong chance the reason she has
134  * a MULTIPOINT EMPTY because we gave it to her during data dump,
135  * converting the internal POINT EMPTY into a EWKB MULTIPOINT EMPTY
136  * (because EWKB doesn't have a clean way to represent POINT EMPTY).
137  * In such a case, it makes sense to turn the MULTIPOINT EMPTY back into a
138  * point EMPTY, rather than throwing an error.
139  */
140  if ( typmod_type == POINTTYPE && geom_type == MULTIPOINTTYPE &&
141  gserialized_is_empty(gser) )
142  {
143  LWPOINT *empty_point = lwpoint_construct_empty(geom_srid, geom_z, geom_m);
144  geom_type = POINTTYPE;
145  pfree(gser);
146  if ( gserialized_is_geodetic(gser) )
147  gser = geography_serialize(lwpoint_as_lwgeom(empty_point));
148  else
149  gser = geometry_serialize(lwpoint_as_lwgeom(empty_point));
150  }
151 
152  /* Typmod has a preference for SRID? Geometry SRID had better match. */
153  if ( typmod_srid > 0 && typmod_srid != geom_srid )
154  {
155  ereport(ERROR, (
156  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
157  errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) ));
158  }
159 
160  /* Typmod has a preference for geometry type. */
161  if ( typmod_type > 0 &&
162  /* GEOMETRYCOLLECTION column can hold any kind of collection */
163  ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE ||
164  geom_type == MULTIPOLYGONTYPE ||
165  geom_type == MULTIPOINTTYPE ||
166  geom_type == MULTILINETYPE )) ||
167  /* Other types must be strictly equal. */
168  (typmod_type != geom_type)) )
169  {
170  ereport(ERROR, (
171  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
172  errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) ));
173  }
174 
175  /* Mismatched Z dimensionality. */
176  if ( typmod_z && ! geom_z )
177  {
178  ereport(ERROR, (
179  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
180  errmsg("Column has Z dimension but geometry does not" )));
181  }
182 
183  /* Mismatched Z dimensionality (other way). */
184  if ( geom_z && ! typmod_z )
185  {
186  ereport(ERROR, (
187  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
188  errmsg("Geometry has Z dimension but column does not" )));
189  }
190 
191  /* Mismatched M dimensionality. */
192  if ( typmod_m && ! geom_m )
193  {
194  ereport(ERROR, (
195  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
196  errmsg("Column has M dimension but geometry does not" )));
197  }
198 
199  /* Mismatched M dimensionality (other way). */
200  if ( geom_m && ! typmod_m )
201  {
202  ereport(ERROR, (
203  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
204  errmsg("Geometry has M dimension but column does not" )));
205  }
206 
207  return gser;
208 
209 }
210 
211 
212 static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
213 {
214  int32 typmod = 0;
215  Datum *elem_values;
216  int n = 0;
217  int i = 0;
218 
219  if (ARR_ELEMTYPE(arr) != CSTRINGOID)
220  ereport(ERROR,
221  (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
222  errmsg("typmod array must be type cstring[]")));
223 
224  if (ARR_NDIM(arr) != 1)
225  ereport(ERROR,
226  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
227  errmsg("typmod array must be one-dimensional")));
228 
229  if (ARR_HASNULL(arr))
230  ereport(ERROR,
231  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
232  errmsg("typmod array must not contain nulls")));
233 
234  deconstruct_array(arr,
235  CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */
236  &elem_values, NULL, &n);
237 
238  /* Set the SRID to the default value first */
239  if ( is_geography)
240  TYPMOD_SET_SRID(typmod, SRID_DEFAULT);
241  else
242  TYPMOD_SET_SRID(typmod, SRID_UNKNOWN);
243 
244  for (i = 0; i < n; i++)
245  {
246  if ( i == 0 ) /* TYPE */
247  {
248  char *s = DatumGetCString(elem_values[i]);
249  uint8_t type = 0;
250  int z = 0;
251  int m = 0;
252 
253  if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE )
254  {
255  ereport(ERROR,
256  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
257  errmsg("Invalid geometry type modifier: %s", s)));
258  }
259  else
260  {
261  TYPMOD_SET_TYPE(typmod, type);
262  if ( z )
263  TYPMOD_SET_Z(typmod);
264  if ( m )
265  TYPMOD_SET_M(typmod);
266  }
267  }
268  if ( i == 1 ) /* SRID */
269  {
270  int srid = pg_atoi(DatumGetCString(elem_values[i]),
271  sizeof(int32), '\0');
272  srid = clamp_srid(srid);
273  POSTGIS_DEBUGF(3, "srid: %d", srid);
274  if ( srid != SRID_UNKNOWN )
275  {
276  TYPMOD_SET_SRID(typmod, srid);
277  }
278  }
279  }
280 
281  pfree(elem_values);
282 
283  return typmod;
284 }
285 
286 /*
287 ** geography_typmod_in(cstring[]) returns int32
288 **
289 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
290 */
292 Datum geography_typmod_in(PG_FUNCTION_ARGS)
293 {
294  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
295  int32 typmod = gserialized_typmod_in(arr, LW_TRUE);
296  int srid = TYPMOD_GET_SRID(typmod);
297  /* Check the SRID is legal (geographic coordinates) */
298  srid_is_latlong(fcinfo, srid);
299 
300  PG_RETURN_INT32(typmod);
301 }
302 
303 /*
304 ** geometry_typmod_in(cstring[]) returns int32
305 **
306 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
307 */
309 Datum geometry_typmod_in(PG_FUNCTION_ARGS)
310 {
311  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
312  uint32 typmod = gserialized_typmod_in(arr, LW_FALSE); /* Not a geography */;
313  PG_RETURN_INT32(typmod);
314 }
315 
316 /*
317 ** geography_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
318 ** Ensure that an incoming geometry conforms to typmod restrictions on
319 ** type, dims and srid.
320 */
322 Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
323 {
324  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
325  int32 typmod = PG_GETARG_INT32(1);
326  /* We don't need to have different behavior based on explicitness. */
327  /* bool isExplicit = PG_GETARG_BOOL(2); */
328 
329  /* Check if geometry typmod is consistent with the supplied one. */
330  arg = postgis_valid_typmod(arg, typmod);
331 
332  PG_RETURN_POINTER(arg);
333 }
334 
335 /*
336 ** geometry_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
337 ** Ensure that an incoming geometry conforms to typmod restrictions on
338 ** type, dims and srid.
339 */
341 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
342 {
343  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
344  int32 typmod = PG_GETARG_INT32(1);
345  /* We don't need to have different behavior based on explicitness. */
346  /* bool isExplicit = PG_GETARG_BOOL(2); */
347 
348  /* Check if geometry typmod is consistent with the supplied one. */
349  arg = postgis_valid_typmod(arg, typmod);
350 
351  PG_RETURN_POINTER(arg);
352 }
353 
354 
355 /*
356 ** postgis_typmod_type(uint32) returns cstring
357 ** Used for geometry_columns and other views on system tables
358 */
360 Datum postgis_typmod_type(PG_FUNCTION_ARGS)
361 {
362  int32 typmod = PG_GETARG_INT32(0);
363  int32 type = TYPMOD_GET_TYPE(typmod);
364  char *s = (char*)palloc(64);
365  char *ptr = s;
366  text *stext;
367 
368  /* Has type? */
369  if ( typmod < 0 || type == 0 )
370  ptr += sprintf(ptr, "Geometry");
371  else
372  ptr += sprintf(ptr, "%s", lwtype_name(type));
373 
374  /* Has Z? */
375  if ( typmod >= 0 && TYPMOD_GET_Z(typmod) )
376  ptr += sprintf(ptr, "%s", "Z");
377 
378  /* Has M? */
379  if ( typmod >= 0 && TYPMOD_GET_M(typmod) )
380  ptr += sprintf(ptr, "%s", "M");
381 
382  stext = cstring_to_text(s);
383  pfree(s);
384  PG_RETURN_TEXT_P(stext);
385 }
386 
387 /*
388 ** postgis_typmod_dims(uint32) returns int
389 ** Used for geometry_columns and other views on system tables
390 */
392 Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
393 {
394  int32 typmod = PG_GETARG_INT32(0);
395  int32 dims = 2;
396  if ( typmod < 0 )
397  PG_RETURN_NULL(); /* unconstrained */
398  if ( TYPMOD_GET_Z(typmod) )
399  dims++;
400  if ( TYPMOD_GET_M(typmod) )
401  dims++;
402  PG_RETURN_INT32(dims);
403 }
404 
405 /*
406 ** postgis_typmod_srid(uint32) returns int
407 ** Used for geometry_columns and other views on system tables
408 */
410 Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
411 {
412  int32 typmod = PG_GETARG_INT32(0);
413  if ( typmod < 0 )
414  PG_RETURN_INT32(0);
415  PG_RETURN_INT32(TYPMOD_GET_SRID(typmod));
416 }
417 
#define TYPMOD_GET_TYPE(typmod)
Definition: liblwgeom.h:166
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:347
PG_FUNCTION_INFO_V1(postgis_typmod_out)
unsigned int int32
Definition: shpopen.c:273
#define TYPMOD_GET_M(typmod)
Definition: liblwgeom.h:170
#define TYPMOD_SET_Z(typmod)
Definition: liblwgeom.h:169
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:164
#define MULTIPOINTTYPE
Definition: liblwgeom.h:87
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:197
#define TYPMOD_SET_M(typmod)
Definition: liblwgeom.h:171
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:178
#define LW_FAILURE
Definition: liblwgeom.h:78
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:76
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:75
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:187
char * s
Definition: cu_in_wkt.c:23
#define TYPMOD_SET_TYPE(typmod, type)
Definition: liblwgeom.h:167
Datum postgis_typmod_type(PG_FUNCTION_ARGS)
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:89
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:84
type
Definition: ovdump.py:41
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:335
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:165
#define TYPMOD_GET_Z(typmod)
Definition: liblwgeom.h:168
#define MULTILINETYPE
Definition: liblwgeom.h:88
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:99
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
This library is the generic geometry handling section of PostGIS.