PostGIS  3.0.6dev-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 
35 #include "utils/elog.h"
36 #include "utils/array.h"
37 #include "utils/builtins.h" /* for pg_atoi */
38 #include "lib/stringinfo.h" /* For binary input */
39 #include "catalog/pg_type.h" /* for CSTRINGOID */
40 
41 #include "liblwgeom.h" /* For standard geometry types. */
42 #include "lwgeom_pg.h" /* For debugging macros. */
43 #include "geography.h" /* For utility functions. */
44 #include "lwgeom_export.h" /* For export 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? Geometry SRID had better match. */
151  if ( typmod_srid > 0 && typmod_srid != geom_srid )
152  {
153  ereport(ERROR, (
154  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
155  errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) ));
156  }
157 
158  /* Typmod has a preference for geometry type. */
159  if ( typmod_type > 0 &&
160  /* GEOMETRYCOLLECTION column can hold any kind of collection */
161  ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE ||
162  geom_type == MULTIPOLYGONTYPE ||
163  geom_type == MULTIPOINTTYPE ||
164  geom_type == MULTILINETYPE )) ||
165  /* Other types must be strictly equal. */
166  (typmod_type != geom_type)) )
167  {
168  ereport(ERROR, (
169  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
170  errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) ));
171  }
172 
173  /* Mismatched Z dimensionality. */
174  if ( typmod_z && ! geom_z )
175  {
176  ereport(ERROR, (
177  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
178  errmsg("Column has Z dimension but geometry does not" )));
179  }
180 
181  /* Mismatched Z dimensionality (other way). */
182  if ( geom_z && ! typmod_z )
183  {
184  ereport(ERROR, (
185  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
186  errmsg("Geometry has Z dimension but column does not" )));
187  }
188 
189  /* Mismatched M dimensionality. */
190  if ( typmod_m && ! geom_m )
191  {
192  ereport(ERROR, (
193  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
194  errmsg("Column has M dimension but geometry does not" )));
195  }
196 
197  /* Mismatched M dimensionality (other way). */
198  if ( geom_m && ! typmod_m )
199  {
200  ereport(ERROR, (
201  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
202  errmsg("Geometry has M dimension but column does not" )));
203  }
204 
205  return gser;
206 
207 }
208 
209 
210 static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography)
211 {
212  int32 typmod = 0;
213  Datum *elem_values;
214  int n = 0;
215  int i = 0;
216 
217  if (ARR_ELEMTYPE(arr) != CSTRINGOID)
218  ereport(ERROR,
219  (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
220  errmsg("typmod array must be type cstring[]")));
221 
222  if (ARR_NDIM(arr) != 1)
223  ereport(ERROR,
224  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
225  errmsg("typmod array must be one-dimensional")));
226 
227  if (ARR_HASNULL(arr))
228  ereport(ERROR,
229  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
230  errmsg("typmod array must not contain nulls")));
231 
232  deconstruct_array(arr,
233  CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */
234  &elem_values, NULL, &n);
235 
236  /* Set the SRID to the default value first */
237  if (is_geography)
238  TYPMOD_SET_SRID(typmod, SRID_DEFAULT);
239  else
240  TYPMOD_SET_SRID(typmod, SRID_UNKNOWN);
241 
242  for (i = 0; i < n; i++)
243  {
244  if ( i == 0 ) /* TYPE */
245  {
246  char *s = DatumGetCString(elem_values[i]);
247  uint8_t type = 0;
248  int z = 0;
249  int m = 0;
250 
251  if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE )
252  {
253  ereport(ERROR,
254  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
255  errmsg("Invalid geometry type modifier: %s", s)));
256  }
257  else
258  {
259  TYPMOD_SET_TYPE(typmod, type);
260  if ( z )
261  TYPMOD_SET_Z(typmod);
262  if ( m )
263  TYPMOD_SET_M(typmod);
264  }
265  }
266  if ( i == 1 ) /* SRID */
267  {
268  int32_t srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0');
269  srid = clamp_srid(srid);
270  POSTGIS_DEBUGF(3, "srid: %d", srid);
271  if ( srid != SRID_UNKNOWN )
272  {
273  TYPMOD_SET_SRID(typmod, srid);
274  }
275  }
276  }
277 
278  pfree(elem_values);
279 
280  return typmod;
281 }
282 
283 /*
284 ** geography_typmod_in(cstring[]) returns int32
285 **
286 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
287 */
289 Datum geography_typmod_in(PG_FUNCTION_ARGS)
290 {
291  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
292  int32 typmod = gserialized_typmod_in(arr, LW_TRUE);
293  int32_t srid = TYPMOD_GET_SRID(typmod);
294  /* Check the SRID is legal (geographic coordinates) */
295  srid_check_latlong(fcinfo, srid);
296 
297  PG_RETURN_INT32(typmod);
298 }
299 
300 /*
301 ** geometry_typmod_in(cstring[]) returns int32
302 **
303 ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3
304 */
306 Datum geometry_typmod_in(PG_FUNCTION_ARGS)
307 {
308  ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0));
309  uint32 typmod = gserialized_typmod_in(arr, LW_FALSE); /* Not a geography */;
310  PG_RETURN_INT32(typmod);
311 }
312 
313 /*
314 ** geography_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
315 ** Ensure that an incoming geometry conforms to typmod restrictions on
316 ** type, dims and srid.
317 */
319 Datum geography_enforce_typmod(PG_FUNCTION_ARGS)
320 {
321  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
322  int32 typmod = PG_GETARG_INT32(1);
323  /* We don't need to have different behavior based on explicitness. */
324  /* bool isExplicit = PG_GETARG_BOOL(2); */
325 
326  /* Check if geometry typmod is consistent with the supplied one. */
327  arg = postgis_valid_typmod(arg, typmod);
328 
329  PG_RETURN_POINTER(arg);
330 }
331 
332 /*
333 ** geometry_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED
334 ** Ensure that an incoming geometry conforms to typmod restrictions on
335 ** type, dims and srid.
336 */
338 Datum geometry_enforce_typmod(PG_FUNCTION_ARGS)
339 {
340  GSERIALIZED *arg = PG_GETARG_GSERIALIZED_P(0);
341  int32 typmod = PG_GETARG_INT32(1);
342  /* We don't need to have different behavior based on explicitness. */
343  /* bool isExplicit = PG_GETARG_BOOL(2); */
344 
345  /* Check if geometry typmod is consistent with the supplied one. */
346  arg = postgis_valid_typmod(arg, typmod);
347 
348  PG_RETURN_POINTER(arg);
349 }
350 
351 
352 /*
353 ** postgis_typmod_type(uint32) returns cstring
354 ** Used for geometry_columns and other views on system tables
355 */
357 Datum postgis_typmod_type(PG_FUNCTION_ARGS)
358 {
359  int32 typmod = PG_GETARG_INT32(0);
360  int32 type = TYPMOD_GET_TYPE(typmod);
361  char *s = (char*)palloc(64);
362  char *ptr = s;
363  text *stext;
364 
365  /* Has type? */
366  if ( typmod < 0 || type == 0 )
367  ptr += sprintf(ptr, "Geometry");
368  else
369  ptr += sprintf(ptr, "%s", lwtype_name(type));
370 
371  /* Has Z? */
372  if ( typmod >= 0 && TYPMOD_GET_Z(typmod) )
373  ptr += sprintf(ptr, "%s", "Z");
374 
375  /* Has M? */
376  if ( typmod >= 0 && TYPMOD_GET_M(typmod) )
377  ptr += sprintf(ptr, "%s", "M");
378 
379  stext = cstring_to_text(s);
380  pfree(s);
381  PG_RETURN_TEXT_P(stext);
382 }
383 
384 /*
385 ** postgis_typmod_dims(uint32) returns int
386 ** Used for geometry_columns and other views on system tables
387 */
389 Datum postgis_typmod_dims(PG_FUNCTION_ARGS)
390 {
391  int32 typmod = PG_GETARG_INT32(0);
392  int32 dims = 2;
393  if ( typmod < 0 )
394  PG_RETURN_NULL(); /* unconstrained */
395  if ( TYPMOD_GET_Z(typmod) )
396  dims++;
397  if ( TYPMOD_GET_M(typmod) )
398  dims++;
399  PG_RETURN_INT32(dims);
400 }
401 
402 /*
403 ** postgis_typmod_srid(uint32) returns int
404 ** Used for geometry_columns and other views on system tables
405 */
407 Datum postgis_typmod_srid(PG_FUNCTION_ARGS)
408 {
409  int32 typmod = PG_GETARG_INT32(0);
410  if ( typmod < 0 )
411  PG_RETURN_INT32(0);
412  PG_RETURN_INT32(TYPMOD_GET_SRID(typmod));
413 }
414 
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
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:152
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:206
#define LW_FALSE
Definition: liblwgeom.h:108
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
#define TYPMOD_SET_SRID(typmod, srid)
Definition: liblwgeom.h:207
#define LW_FAILURE
Definition: liblwgeom.h:110
#define MULTILINETYPE
Definition: liblwgeom.h:120
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
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:212
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:121
#define TYPMOD_SET_TYPE(typmod, type)
Definition: liblwgeom.h:209
#define SRID_DEFAULT
Definition: liblwgeom.h:239
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:326
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:208
#define TYPMOD_SET_M(typmod)
Definition: liblwgeom.h:213
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:107
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:229
#define TYPMOD_SET_Z(typmod)
Definition: liblwgeom.h:211
#define TYPMOD_GET_Z(typmod)
Definition: liblwgeom.h:210
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
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
unsigned int int32
Definition: shpopen.c:273