From 15d20908c22f48517e6a7f1388ae5229f18a3a24 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Thu, 16 Jun 2011 21:01:53 +0000 Subject: [PATCH] Partial typmod support for PostGIS geometry (#944) git-svn-id: http://svn.osgeo.org/postgis/trunk@7409 b70326c6-7e19-0410-871a-916f4a2858ee --- postgis/Makefile.in | 1 + postgis/geography.h | 3 +- postgis/geography.sql.in.c | 21 +- postgis/geography_inout.c | 336 ++-------------------------- postgis/geometry_inout.c | 0 postgis/gserialized_typmod.c | 422 +++++++++++++++++++++++++++++++++++ postgis/postgis.sql.in.c | 68 ++++++ 7 files changed, 523 insertions(+), 328 deletions(-) create mode 100644 postgis/geometry_inout.c create mode 100644 postgis/gserialized_typmod.c diff --git a/postgis/Makefile.in b/postgis/Makefile.in index 8f9305275..240f83a2c 100644 --- a/postgis/Makefile.in +++ b/postgis/Makefile.in @@ -55,6 +55,7 @@ PG_OBJS=lwgeom_pg.o \ lwgeom_rtree.o \ geography_inout.o \ gserialized_gist.o \ + gserialized_typmod.o \ gserialized_gist_2d.o \ gserialized_gist_nd.o \ geography_btree.o \ diff --git a/postgis/geography.h b/postgis/geography.h index 6a3af7619..f9b196c2e 100644 --- a/postgis/geography.h +++ b/postgis/geography.h @@ -36,8 +36,9 @@ /* Convert lwgeom to newly allocated gserialized */ GSERIALIZED* geography_serialize(LWGEOM *lwgeom); +GSERIALIZED* geometry_serialize(LWGEOM *lwgeom); /* Check that the typmod matches the flags on the lwgeom */ -void geography_valid_typmod(LWGEOM *lwgeom, int32 typmod); +void postgis_valid_typmod(LWGEOM *lwgeom, int32 typmod); /* Check that the type is legal in geography (no curves please!) */ void geography_valid_type(uchar type); diff --git a/postgis/geography.sql.in.c b/postgis/geography.sql.in.c index 29c6a4caa..b860a5c4b 100644 --- a/postgis/geography.sql.in.c +++ b/postgis/geography.sql.in.c @@ -18,7 +18,7 @@ CREATE OR REPLACE FUNCTION geography_typmod_in(cstring[]) -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION geography_typmod_out(integer) RETURNS cstring - AS 'MODULE_PATHNAME','geography_typmod_out' + AS 'MODULE_PATHNAME','postgis_typmod_out' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 @@ -46,6 +46,7 @@ CREATE TYPE geography ( output = geography_out, typmod_in = geography_typmod_in, typmod_out = geography_typmod_out, + delimiter = ':', analyze = geography_analyze, storage = main, alignment = double @@ -131,21 +132,21 @@ CREATE OR REPLACE FUNCTION ST_GeogFromWKB(bytea) LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 -CREATE OR REPLACE FUNCTION geography_typmod_dims(integer) +CREATE OR REPLACE FUNCTION postgis_typmod_dims(integer) RETURNS integer - AS 'MODULE_PATHNAME','geography_typmod_dims' + AS 'MODULE_PATHNAME','postgis_typmod_dims' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 -CREATE OR REPLACE FUNCTION geography_typmod_srid(integer) +CREATE OR REPLACE FUNCTION postgis_typmod_srid(integer) RETURNS integer - AS 'MODULE_PATHNAME','geography_typmod_srid' + AS 'MODULE_PATHNAME','postgis_typmod_srid' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 -CREATE OR REPLACE FUNCTION geography_typmod_type(integer) +CREATE OR REPLACE FUNCTION postgis_typmod_type(integer) RETURNS text - AS 'MODULE_PATHNAME','geography_typmod_type' + AS 'MODULE_PATHNAME','postgis_typmod_type' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 @@ -155,9 +156,9 @@ CREATE OR REPLACE VIEW geography_columns AS n.nspname AS f_table_schema, c.relname AS f_table_name, a.attname AS f_geography_column, - geography_typmod_dims(a.atttypmod) AS coord_dimension, - geography_typmod_srid(a.atttypmod) AS srid, - geography_typmod_type(a.atttypmod) AS type + postgis_typmod_dims(a.atttypmod) AS coord_dimension, + postgis_typmod_srid(a.atttypmod) AS srid, + postgis_typmod_type(a.atttypmod) AS type FROM pg_class c, pg_attribute a, diff --git a/postgis/geography_inout.c b/postgis/geography_inout.c index 865a6cbdd..9c3fc98ee 100644 --- a/postgis/geography_inout.c +++ b/postgis/geography_inout.c @@ -32,13 +32,7 @@ Datum geography_in(PG_FUNCTION_ARGS); Datum geography_out(PG_FUNCTION_ARGS); -Datum geography_typmod_in(PG_FUNCTION_ARGS); -Datum geography_typmod_out(PG_FUNCTION_ARGS); -Datum geography_typmod_dims(PG_FUNCTION_ARGS); -Datum geography_typmod_srid(PG_FUNCTION_ARGS); -Datum geography_typmod_type(PG_FUNCTION_ARGS); -Datum geography_enforce_typmod(PG_FUNCTION_ARGS); Datum geography_as_text(PG_FUNCTION_ARGS); Datum geography_from_text(PG_FUNCTION_ARGS); Datum geography_as_geojson(PG_FUNCTION_ARGS); @@ -72,6 +66,24 @@ GSERIALIZED* geography_serialize(LWGEOM *lwgeom) return g; } + +/** +* Utility method to call the serialization and then set the +* PgSQL varsize header appropriately with the serialized size. +*/ +GSERIALIZED* geometry_serialize(LWGEOM *lwgeom) +{ + static int is_geodetic = 0; + size_t ret_size = 0; + GSERIALIZED *g = NULL; + + g = gserialized_from_lwgeom(lwgeom, is_geodetic, &ret_size); + if ( ! g ) lwerror("Unable to serialize lwgeom."); + SET_VARSIZE(g, ret_size); + return g; +} + + /** * The geography type only support POINT, LINESTRING, POLYGON, MULTI* variants * of same, and GEOMETRYCOLLECTION. If the input type is not one of those, shut @@ -96,91 +108,6 @@ void geography_valid_type(uchar type) } } -/** -* 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 geography_valid_typmod(LWGEOM *lwgeom, int32 typmod) -{ - int32 lwgeom_srid; - int32 lwgeom_type; - int32 lwgeom_z; - int32 lwgeom_m; - 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); - - Assert(lwgeom); - - lwgeom_type = lwgeom->type; - lwgeom_srid = lwgeom->srid; - lwgeom_z = FLAGS_GET_Z(lwgeom->flags); - lwgeom_m = FLAGS_GET_M(lwgeom->flags); - - POSTGIS_DEBUG(2, "Entered function"); - - /* No typmod (-1) => no preferences */ - if (typmod < 0) return; - - POSTGIS_DEBUGF(3, "Got lwgeom(type = %d, srid = %d, hasz = %d, hasm = %d)", lwgeom_type, lwgeom_srid, lwgeom_z, lwgeom_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 lwgeom has a non-default SRID? They had better match. */ - if ( typmod_srid > 0 && typmod_srid != lwgeom_srid ) - { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Geography SRID (%d) does not match column SRID (%d)", lwgeom_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 && ! (lwgeom_type == COLLECTIONTYPE || - lwgeom_type == MULTIPOLYGONTYPE || - lwgeom_type == MULTIPOINTTYPE || - lwgeom_type == MULTILINETYPE )) || - /* Other types must be strictly equal. */ - (typmod_type != lwgeom_type)) ) - { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(lwgeom_type), lwtype_name(typmod_type)) )); - } - - /* Mismatched Z dimensionality. */ - if ( typmod_z && ! lwgeom_z ) - { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Column has Z dimension but geometry does not" ))); - } - - /* Mismatched Z dimensionality (other way). */ - if ( lwgeom_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 && ! lwgeom_m ) - { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Column has M dimension but geometry does not" ))); - } - - /* Mismatched M dimensionality (other way). */ - if ( lwgeom_m && ! typmod_m ) - { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Geometry has M dimension but column does not" ))); - } -} /* ** geography_in(cstring) returns *GSERIALIZED @@ -239,7 +166,7 @@ Datum geography_in(PG_FUNCTION_ARGS) if ( geog_typmod >= 0 ) { - geography_valid_typmod(lwgeom, geog_typmod); + postgis_valid_typmod(lwgeom, geog_typmod); POSTGIS_DEBUG(3, "typmod and geometry were consistent"); } else @@ -278,183 +205,6 @@ Datum geography_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(hexwkb); } -/* -** geography_enforce_typmod(*GSERIALIZED) returns *GSERIALIZED -*/ -PG_FUNCTION_INFO_V1(geography_enforce_typmod); -Datum geography_enforce_typmod(PG_FUNCTION_ARGS) -{ - GSERIALIZED *arg = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); - LWGEOM *lwgeom = NULL; - int32 typmod = PG_GETARG_INT32(1); - /* We don't need to have different behavior based on explicitness. */ - /* bool isExplicit = PG_GETARG_BOOL(2); */ - - lwgeom = lwgeom_from_gserialized(arg); - - /* Check if geometry typmod is consistent with the supplied one. */ - geography_valid_typmod(lwgeom, typmod); - - PG_RETURN_POINTER(geography_serialize(lwgeom)); -} - -/* -** 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 = 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 */ - TYPMOD_SET_SRID(typmod, SRID_DEFAULT); - - for (i = 0; i < n; i++) - { - if ( i == 1 ) /* SRID */ - { - int srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); - if ( srid > 0 ) - { - POSTGIS_DEBUGF(3, "srid: %d", srid); - if ( srid > SRID_MAXIMUM ) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("SRID value may not exceed %d", - SRID_MAXIMUM))); - } - else - { - /* TODO: Check that the value provided is in fact a lonlat entry in spatial_ref_sys. */ - /* For now, we only accept SRID_DEFAULT. */ - if ( 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); - } - } - } - else - { - } - } - if ( i == 0 ) /* TYPE */ - { - char *s = DatumGetCString(elem_values[i]); - int 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); - } - } - } - - pfree(elem_values); - - PG_RETURN_INT32(typmod); - -} - - -/* -** geography_typmod_out(int) returns cstring -*/ -PG_FUNCTION_INFO_V1(geography_typmod_out); -Datum geography_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); - -} /* ** geography_as_text(*GSERIALIZED) returns text @@ -862,56 +612,8 @@ Datum geography_from_binary(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall3(geography_in, PointerGetDatum(wkb_cstring), Int32GetDatum(0), Int32GetDatum(-1))); } -PG_FUNCTION_INFO_V1(geography_typmod_dims); -Datum geography_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); -} - -PG_FUNCTION_INFO_V1(geography_typmod_srid); -Datum geography_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)); -} -PG_FUNCTION_INFO_V1(geography_typmod_type); -Datum geography_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); -} PG_FUNCTION_INFO_V1(geography_from_geometry); Datum geography_from_geometry(PG_FUNCTION_ARGS) diff --git a/postgis/geometry_inout.c b/postgis/geometry_inout.c new file mode 100644 index 000000000..e69de29bb diff --git a/postgis/gserialized_typmod.c b/postgis/gserialized_typmod.c new file mode 100644 index 000000000..67a8595d0 --- /dev/null +++ b/postgis/gserialized_typmod.c @@ -0,0 +1,422 @@ +/********************************************************************** + * $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(LWGEOM *lwgeom, int32 typmod) +{ + int32 lwgeom_srid; + int32 lwgeom_type; + int32 lwgeom_z; + int32 lwgeom_m; + 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); + + Assert(lwgeom); + + lwgeom_type = lwgeom->type; + lwgeom_srid = lwgeom->srid; + lwgeom_z = FLAGS_GET_Z(lwgeom->flags); + lwgeom_m = FLAGS_GET_M(lwgeom->flags); + + POSTGIS_DEBUG(2, "Entered function"); + + /* No typmod (-1) => no preferences */ + if (typmod < 0) return; + + POSTGIS_DEBUGF(3, "Got lwgeom(type = %d, srid = %d, hasz = %d, hasm = %d)", lwgeom_type, lwgeom_srid, lwgeom_z, lwgeom_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 lwgeom has a non-default SRID? They had better match. */ + if ( typmod_srid > 0 && typmod_srid != lwgeom_srid ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Geography SRID (%d) does not match column SRID (%d)", lwgeom_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 && ! (lwgeom_type == COLLECTIONTYPE || + lwgeom_type == MULTIPOLYGONTYPE || + lwgeom_type == MULTIPOINTTYPE || + lwgeom_type == MULTILINETYPE )) || + /* Other types must be strictly equal. */ + (typmod_type != lwgeom_type)) ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(lwgeom_type), lwtype_name(typmod_type)) )); + } + + /* Mismatched Z dimensionality. */ + if ( typmod_z && ! lwgeom_z ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Column has Z dimension but geometry does not" ))); + } + + /* Mismatched Z dimensionality (other way). */ + if ( lwgeom_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 && ! lwgeom_m ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Column has M dimension but geometry does not" ))); + } + + /* Mismatched M dimensionality (other way). */ + if ( lwgeom_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]); + int 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'); + if ( srid > 0 ) + { + POSTGIS_DEBUGF(3, "srid: %d", srid); + if ( srid > SRID_MAXIMUM ) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("SRID value may not exceed %d", + SRID_MAXIMUM))); + } + else + { + /* TODO: Check that the value provided is in fact a lonlat entry in spatial_ref_sys. */ + /* 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); + } + } + } + else + { + if ( is_geography ) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Negative SRID is not for GEOGRAPHY"))); + } + } + } + } + + 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)); + LWGEOM *lwgeom = NULL; + int32 typmod = PG_GETARG_INT32(1); + /* We don't need to have different behavior based on explicitness. */ + /* bool isExplicit = PG_GETARG_BOOL(2); */ + + lwgeom = lwgeom_from_gserialized(arg); + + /* Check if geometry typmod is consistent with the supplied one. */ + postgis_valid_typmod(lwgeom, typmod); + + PG_RETURN_POINTER(geography_serialize(lwgeom)); +} + +/* +** 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)); + LWGEOM *lwgeom = NULL; + int32 typmod = PG_GETARG_INT32(1); + /* We don't need to have different behavior based on explicitness. */ + /* bool isExplicit = PG_GETARG_BOOL(2); */ + + lwgeom = lwgeom_from_gserialized(arg); + + /* Check if geometry typmod is consistent with the supplied one. */ + postgis_valid_typmod(lwgeom, typmod); + + PG_RETURN_POINTER(geometry_serialize(lwgeom)); +} + + +/* +** 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)); +} + diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index 1431224c2..d6b8f0334 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -61,6 +61,25 @@ CREATE OR REPLACE FUNCTION geometry_out(geometry) AS 'MODULE_PATHNAME','LWGEOM_out' LANGUAGE 'C' IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION geometry_out(geometry) + RETURNS cstring + AS 'MODULE_PATHNAME','LWGEOM_out' + LANGUAGE 'C' IMMUTABLE STRICT; + +#ifdef GSERIALIZED_ON +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_typmod_in(cstring[]) + RETURNS integer + AS 'MODULE_PATHNAME','geometry_typmod_in' + LANGUAGE 'C' IMMUTABLE STRICT; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_typmod_out(integer) + RETURNS cstring + AS 'MODULE_PATHNAME','postgis_typmod_out' + LANGUAGE 'C' IMMUTABLE STRICT; +#endif + CREATE OR REPLACE FUNCTION geometry_analyze(internal) RETURNS bool #ifdef GSERIALIZED_ON @@ -86,11 +105,28 @@ CREATE TYPE geometry ( output = geometry_out, send = geometry_send, receive = geometry_recv, +#ifdef GSERIALIZED_ON + typmod_in = geometry_typmod_in, + typmod_out = geometry_typmod_out, delimiter = ':', + alignment = double, +#endif analyze = geometry_analyze, storage = main ); + +-- Availability: 2.0.0 +-- Special cast for enforcing the typmod restrictions +CREATE OR REPLACE FUNCTION geometry(geometry, integer, boolean) + RETURNS geometry + AS 'MODULE_PATHNAME','geometry_enforce_typmod' + LANGUAGE 'C' IMMUTABLE STRICT; + +-- Availability: 2.0.0 +CREATE CAST (geometry AS geometry) WITH FUNCTION geometry(geometry, integer, boolean) AS IMPLICIT; + + ------------------------------------------- -- Affine transforms ------------------------------------------- @@ -1571,6 +1607,7 @@ CREATE TABLE geometry_columns ( f_geometry_column ) ) WITH OIDS; + ----------------------------------------------------------------------- -- RENAME_GEOMETRY_TABLE_CONSTRAINTS() ----------------------------------------------------------------------- @@ -4750,6 +4787,37 @@ LANGUAGE 'plpgsql' IMMUTABLE STRICT; #include "geography.sql.in.c" + + + +#ifdef GSERIALIZED_ON +--------------------------------------------------------------- +-- GEOMETRY_COLUMNS view +--------------------------------------------------------------- +CREATE OR REPLACE VIEW geometry_columns_v AS + SELECT + current_database() AS f_table_catalog, + n.nspname AS f_table_schema, + c.relname AS f_table_name, + a.attname AS f_geography_column, + postgis_typmod_dims(a.atttypmod) AS coord_dimension, + postgis_typmod_srid(a.atttypmod) AS srid, + postgis_typmod_type(a.atttypmod) AS type + FROM + pg_class c, + pg_attribute a, + pg_type t, + pg_namespace n + WHERE t.typname = 'geometry' + AND a.attisdropped = false + AND a.atttypid = t.oid + AND a.attrelid = c.oid + AND c.relnamespace = n.oid + AND NOT pg_is_other_temp_schema(c.relnamespace); +#endif + + + --------------------------------------------------------------- -- 3D-functions --------------------------------------------------------------- -- 2.50.1