PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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
48Datum geography_typmod_in(PG_FUNCTION_ARGS);
49Datum geometry_typmod_in(PG_FUNCTION_ARGS);
50Datum postgis_typmod_out(PG_FUNCTION_ARGS);
51Datum postgis_typmod_dims(PG_FUNCTION_ARGS);
52Datum postgis_typmod_srid(PG_FUNCTION_ARGS);
53Datum postgis_typmod_type(PG_FUNCTION_ARGS);
54Datum geography_enforce_typmod(PG_FUNCTION_ARGS);
55Datum geometry_enforce_typmod(PG_FUNCTION_ARGS);
56
57
58/*
59** postgis_typmod_out(int) returns cstring
60*/
62Datum 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 &&
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
234static 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)
263 else
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
336*/
338Datum 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
353*/
355Datum 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*/
368Datum 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*/
387Datum 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*/
406Datum 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*/
438Datum 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*/
456Datum 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)...
int gserialized_is_geodetic(const GSERIALIZED *g)
Check if a GSERIALIZED is a geography.
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
int gserialized_has_m(const GSERIALIZED *g)
Check if a GSERIALIZED has an M ordinate.
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
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).
int gserialized_has_z(const GSERIALIZED *g)
Check if a GSERIALIZED has a Z ordinate.
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
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)
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_SRID(typmod)
Macros for manipulating the 'typemod' int.
Definition liblwgeom.h:192
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:372
#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:1246
LWGEOM * lwgeom_as_multi(const LWGEOM *lwgeom)
Create a new LWGEOM of the appropriate MULTI* type.
Definition lwgeom.c:408
#define MULTILINETYPE
Definition liblwgeom.h:106
#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:495
#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
uint8_t lwtype_multitype(uint8_t type)
Definition lwgeom.c:398
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoint.c:151
#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:339
This library is the generic geometry handling section of PostGIS.
unsigned int int32
Definition shpopen.c:54