/********************************************************************** * $Id: geography_inout.c 7248 2011-05-25 18:42:16Z pramsey $ * * PostGIS - Spatial Types for PostgreSQL * Copyright 2009 Paul Ramsey * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include "postgres.h" #include "../postgis_config.h" #include #include #include #include #include #include "utils/elog.h" #include "utils/array.h" #include "utils/builtins.h" /* for pg_atoi */ #include "lib/stringinfo.h" /* For binary input */ #include "catalog/pg_type.h" /* for CSTRINGOID */ #include "liblwgeom.h" /* For standard geometry types. */ #include "lwgeom_pg.h" /* For debugging macros. */ #include "geography.h" /* For utility functions. */ #include "lwgeom_export.h" /* For export functions. */ Datum geography_typmod_in(PG_FUNCTION_ARGS); Datum geometry_typmod_in(PG_FUNCTION_ARGS); Datum postgis_typmod_out(PG_FUNCTION_ARGS); Datum postgis_typmod_dims(PG_FUNCTION_ARGS); Datum postgis_typmod_srid(PG_FUNCTION_ARGS); Datum postgis_typmod_type(PG_FUNCTION_ARGS); Datum geography_enforce_typmod(PG_FUNCTION_ARGS); Datum geometry_enforce_typmod(PG_FUNCTION_ARGS); /* ** postgis_typmod_out(int) returns cstring */ PG_FUNCTION_INFO_V1(postgis_typmod_out); Datum postgis_typmod_out(PG_FUNCTION_ARGS) { char *s = (char*)palloc(64); char *str = s; uint32 typmod = PG_GETARG_INT32(0); uint32 srid = TYPMOD_GET_SRID(typmod); uint32 type = TYPMOD_GET_TYPE(typmod); uint32 hasz = TYPMOD_GET_Z(typmod); uint32 hasm = TYPMOD_GET_M(typmod); POSTGIS_DEBUGF(3, "Got typmod(srid = %d, type = %d, hasz = %d, hasm = %d)", srid, type, hasz, hasm); /* No SRID or type or dimensionality? Then no typmod at all. Return empty string. */ if ( ! ( srid || type || hasz || hasz ) ) { *str = '\0'; PG_RETURN_CSTRING(str); } /* Opening bracket. */ str += sprintf(str, "("); /* Has type? */ if ( type ) str += sprintf(str, "%s", lwtype_name(type)); else if ( (!type) && ( srid || hasz || hasm ) ) str += sprintf(str, "Geometry"); /* Has Z? */ if ( hasz ) str += sprintf(str, "%s", "Z"); /* Has M? */ if ( hasm ) str += sprintf(str, "%s", "M"); /* Comma? */ if ( srid ) str += sprintf(str, ","); /* Has SRID? */ if ( srid ) str += sprintf(str, "%d", srid); /* Closing bracket. */ str += sprintf(str, ")"); PG_RETURN_CSTRING(s); } /** * Check the consistency of the metadata we want to enforce in the typmod: * srid, type and dimensionality. If things are inconsistent, shut down the query. */ void postgis_valid_typmod(const GSERIALIZED *gser, int32_t typmod) { int32 geom_srid = gserialized_get_srid(gser); int32 geom_type = gserialized_get_type(gser); int32 geom_z = gserialized_has_z(gser); int32 geom_m = gserialized_has_m(gser); int32 typmod_srid = TYPMOD_GET_SRID(typmod); int32 typmod_type = TYPMOD_GET_TYPE(typmod); int32 typmod_z = TYPMOD_GET_Z(typmod); int32 typmod_m = TYPMOD_GET_M(typmod); POSTGIS_DEBUG(2, "Entered function"); /* No typmod (-1) => no preferences */ if (typmod < 0) return; POSTGIS_DEBUGF(3, "Got geom(type = %d, srid = %d, hasz = %d, hasm = %d)", geom_type, geom_srid, geom_z, geom_m); POSTGIS_DEBUGF(3, "Got typmod(type = %d, srid = %d, hasz = %d, hasm = %d)", typmod_type, typmod_srid, typmod_z, typmod_m); /* Typmod has a preference for SRID and geom has a non-default SRID? They had better match. */ if ( typmod_srid > 0 && typmod_srid != geom_srid ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) )); } /* Typmod has a preference for geometry type. */ if ( typmod_type > 0 && /* GEOMETRYCOLLECTION column can hold any kind of collection */ ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE || geom_type == MULTIPOLYGONTYPE || geom_type == MULTIPOINTTYPE || geom_type == MULTILINETYPE )) || /* Other types must be strictly equal. */ (typmod_type != geom_type)) ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) )); } /* Mismatched Z dimensionality. */ if ( typmod_z && ! geom_z ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Column has Z dimension but geometry does not" ))); } /* Mismatched Z dimensionality (other way). */ if ( geom_z && ! typmod_z ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry has Z dimension but column does not" ))); } /* Mismatched M dimensionality. */ if ( typmod_m && ! geom_m ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Column has M dimension but geometry does not" ))); } /* Mismatched M dimensionality (other way). */ if ( geom_m && ! typmod_m ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry has M dimension but column does not" ))); } } static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 0; if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); /* Set the SRID to the default value first */ if ( is_geography) TYPMOD_SET_SRID(typmod, SRID_DEFAULT); else TYPMOD_SET_SRID(typmod, SRID_UNKNOWN); for (i = 0; i < n; i++) { if ( i == 0 ) /* TYPE */ { char *s = DatumGetCString(elem_values[i]); uint8_t type = 0; int z = 0; int m = 0; if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid geometry type modifier: %s", s))); } else { TYPMOD_SET_TYPE(typmod, type); if ( z ) TYPMOD_SET_Z(typmod); if ( m ) TYPMOD_SET_M(typmod); } } if ( i == 1 ) /* SRID */ { int srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); srid = clamp_srid(srid); POSTGIS_DEBUGF(3, "srid: %d", srid); if ( srid != SRID_UNKNOWN ) { /* TODO: Check that the value * provided is in fact a lonlat * entry in spatial_ref_sysj */ /* For now, we only accept SRID_DEFAULT. */ if ( is_geography && srid != SRID_DEFAULT ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Currently, only %d is accepted as an SRID for GEOGRAPHY", SRID_DEFAULT))); } else { TYPMOD_SET_SRID(typmod, srid); } } #if 0 /* keep the default instead */ else { if ( is_geography ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("UNKNOWN SRID is not for GEOGRAPHY"))); } } #endif /* keep the default for unknown */ } } pfree(elem_values); return typmod; } /* ** geography_typmod_in(cstring[]) returns int32 ** ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3 */ PG_FUNCTION_INFO_V1(geography_typmod_in); Datum geography_typmod_in(PG_FUNCTION_ARGS) { ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0)); uint32 typmod = gserialized_typmod_in(arr, LW_TRUE); PG_RETURN_INT32(typmod); } /* ** geometry_typmod_in(cstring[]) returns int32 ** ** Modified from ArrayGetIntegerTypmods in PostgreSQL 8.3 */ PG_FUNCTION_INFO_V1(geometry_typmod_in); Datum geometry_typmod_in(PG_FUNCTION_ARGS) { ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0)); uint32 typmod = gserialized_typmod_in(arr, LW_FALSE); /* Not a geography */; PG_RETURN_INT32(typmod); } /* ** geography_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED ** Ensure that an incoming geometry conforms to typmod restrictions on ** type, dims and srid. */ PG_FUNCTION_INFO_V1(geography_enforce_typmod); Datum geography_enforce_typmod(PG_FUNCTION_ARGS) { GSERIALIZED *arg = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); int32 typmod = PG_GETARG_INT32(1); /* We don't need to have different behavior based on explicitness. */ /* bool isExplicit = PG_GETARG_BOOL(2); */ /* Check if geometry typmod is consistent with the supplied one. */ postgis_valid_typmod(arg, typmod); PG_RETURN_POINTER(arg); } /* ** geometry_enforce_typmod(*GSERIALIZED, uint32) returns *GSERIALIZED ** Ensure that an incoming geometry conforms to typmod restrictions on ** type, dims and srid. */ PG_FUNCTION_INFO_V1(geometry_enforce_typmod); Datum geometry_enforce_typmod(PG_FUNCTION_ARGS) { GSERIALIZED *arg = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); int32 typmod = PG_GETARG_INT32(1); /* We don't need to have different behavior based on explicitness. */ /* bool isExplicit = PG_GETARG_BOOL(2); */ /* Check if geometry typmod is consistent with the supplied one. */ postgis_valid_typmod(arg, typmod); PG_RETURN_POINTER(arg); } /* ** postgis_typmod_type(uint32) returns cstring ** Used for geometry_columns and other views on system tables */ PG_FUNCTION_INFO_V1(postgis_typmod_type); Datum postgis_typmod_type(PG_FUNCTION_ARGS) { int32 typmod = PG_GETARG_INT32(0); int32 type = TYPMOD_GET_TYPE(typmod); char *s = (char*)palloc(64); char *ptr = s; text *stext; /* Has type? */ if ( typmod < 0 || type == 0 ) ptr += sprintf(ptr, "Geometry"); else ptr += sprintf(ptr, "%s", lwtype_name(type)); /* Has Z? */ if ( typmod >= 0 && TYPMOD_GET_Z(typmod) ) ptr += sprintf(ptr, "%s", "Z"); /* Has M? */ if ( typmod >= 0 && TYPMOD_GET_M(typmod) ) ptr += sprintf(ptr, "%s", "M"); stext = cstring2text(s); pfree(s); PG_RETURN_TEXT_P(stext); } /* ** postgis_typmod_dims(uint32) returns int ** Used for geometry_columns and other views on system tables */ PG_FUNCTION_INFO_V1(postgis_typmod_dims); Datum postgis_typmod_dims(PG_FUNCTION_ARGS) { int32 typmod = PG_GETARG_INT32(0); int32 dims = 2; if ( typmod < 0 ) PG_RETURN_INT32(dims); if ( TYPMOD_GET_Z(typmod) ) dims++; if ( TYPMOD_GET_M(typmod) ) dims++; PG_RETURN_INT32(dims); } /* ** postgis_typmod_srid(uint32) returns int ** Used for geometry_columns and other views on system tables */ PG_FUNCTION_INFO_V1(postgis_typmod_srid); Datum postgis_typmod_srid(PG_FUNCTION_ARGS) { int32 typmod = PG_GETARG_INT32(0); if ( typmod < 0 ) PG_RETURN_INT32(0); PG_RETURN_INT32(TYPMOD_GET_SRID(typmod)); }