]> granicus.if.org Git - postgis/commitdiff
Partial typmod support for PostGIS geometry (#944)
authorPaul Ramsey <pramsey@cleverelephant.ca>
Thu, 16 Jun 2011 21:01:53 +0000 (21:01 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Thu, 16 Jun 2011 21:01:53 +0000 (21:01 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@7409 b70326c6-7e19-0410-871a-916f4a2858ee

postgis/Makefile.in
postgis/geography.h
postgis/geography.sql.in.c
postgis/geography_inout.c
postgis/geometry_inout.c [new file with mode: 0644]
postgis/gserialized_typmod.c [new file with mode: 0644]
postgis/postgis.sql.in.c

index 8f930527590a7c3941d7d40278c498b9d9ea1923..240f83a2ce53136cd840d62a5622e2729f43703b 100644 (file)
@@ -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 \
index 6a3af7619d3f51b69d0acf66bc03e5339ec34854..f9b196c2e3569c4042ce2a60dca656efaf72442d 100644 (file)
@@ -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);
 
index 29c6a4caa11243e3f6a26d29023dbbb749d0b1dd..b860a5c4bd7420ba457c2a1e5197830ed309eae4 100644 (file)
@@ -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, 
index 865a6cbddd437b07373fb8b2466a2b1a483eb129..9c3fc98ee26a2e8aecefef6a97268325d7b7dc7d 100644 (file)
 
 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 (file)
index 0000000..e69de29
diff --git a/postgis/gserialized_typmod.c b/postgis/gserialized_typmod.c
new file mode 100644 (file)
index 0000000..67a8595
--- /dev/null
@@ -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 <pramsey@cleverelephant.ca>
+ *
+ * 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 <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#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));
+}
+
index 1431224c21ed6d2769f78490c8a2875a3823623f..d6b8f0334d4c654ae9be3811e74ef18cb633f11e 100644 (file)
@@ -61,6 +61,25 @@ CREATE OR REPLACE FUNCTION geometry_out(geometry)
        AS 'MODULE_PATHNAME','LWGEOM_out'\r
        LANGUAGE 'C' IMMUTABLE STRICT;\r
 \r
+CREATE OR REPLACE FUNCTION geometry_out(geometry)\r
+       RETURNS cstring\r
+       AS 'MODULE_PATHNAME','LWGEOM_out'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+#ifdef GSERIALIZED_ON\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_typmod_in(cstring[])\r
+       RETURNS integer\r
+       AS 'MODULE_PATHNAME','geometry_typmod_in'\r
+       LANGUAGE 'C' IMMUTABLE STRICT; \r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_typmod_out(integer)\r
+       RETURNS cstring\r
+       AS 'MODULE_PATHNAME','postgis_typmod_out'\r
+       LANGUAGE 'C' IMMUTABLE STRICT; \r
+#endif\r
+\r
 CREATE OR REPLACE FUNCTION geometry_analyze(internal)\r
        RETURNS bool\r
 #ifdef GSERIALIZED_ON\r
@@ -86,11 +105,28 @@ CREATE TYPE geometry (
        output = geometry_out,\r
        send = geometry_send,\r
        receive = geometry_recv,\r
+#ifdef GSERIALIZED_ON\r
+       typmod_in = geometry_typmod_in,\r
+       typmod_out = geometry_typmod_out,\r
        delimiter = ':',\r
+    alignment = double,\r
+#endif\r
        analyze = geometry_analyze,\r
        storage = main\r
 );\r
 \r
+\r
+-- Availability: 2.0.0\r
+-- Special cast for enforcing the typmod restrictions\r
+CREATE OR REPLACE FUNCTION geometry(geometry, integer, boolean)\r
+       RETURNS geometry\r
+       AS 'MODULE_PATHNAME','geometry_enforce_typmod'\r
+       LANGUAGE 'C' IMMUTABLE STRICT; \r
+\r
+-- Availability: 2.0.0\r
+CREATE CAST (geometry AS geometry) WITH FUNCTION geometry(geometry, integer, boolean) AS IMPLICIT;\r
+\r
+\r
 -------------------------------------------\r
 -- Affine transforms\r
 -------------------------------------------\r
@@ -1571,6 +1607,7 @@ CREATE TABLE geometry_columns (
                f_geometry_column )\r
 ) WITH OIDS;\r
 \r
+\r
 -----------------------------------------------------------------------\r
 -- RENAME_GEOMETRY_TABLE_CONSTRAINTS()\r
 -----------------------------------------------------------------------\r
@@ -4750,6 +4787,37 @@ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 #include "geography.sql.in.c"\r
 \r
 \r
+\r
+\r
+\r
+#ifdef GSERIALIZED_ON\r
+---------------------------------------------------------------\r
+-- GEOMETRY_COLUMNS view\r
+---------------------------------------------------------------\r
+CREATE OR REPLACE VIEW geometry_columns_v AS\r
+       SELECT\r
+               current_database() AS f_table_catalog, \r
+               n.nspname AS f_table_schema, \r
+               c.relname AS f_table_name, \r
+               a.attname AS f_geography_column,\r
+               postgis_typmod_dims(a.atttypmod) AS coord_dimension,\r
+               postgis_typmod_srid(a.atttypmod) AS srid,\r
+               postgis_typmod_type(a.atttypmod) AS type\r
+       FROM \r
+               pg_class c, \r
+               pg_attribute a, \r
+               pg_type t, \r
+               pg_namespace n\r
+       WHERE t.typname = 'geometry'\r
+        AND a.attisdropped = false\r
+        AND a.atttypid = t.oid\r
+        AND a.attrelid = c.oid\r
+        AND c.relnamespace = n.oid\r
+        AND NOT pg_is_other_temp_schema(c.relnamespace);\r
+#endif\r
+\r
+\r
+\r
 ---------------------------------------------------------------\r
 -- 3D-functions\r
 ---------------------------------------------------------------\r