[postgis-commits] svn - r2779 - trunk/topology

postgis-commits at postgis.refractions.net postgis-commits at postgis.refractions.net
Thu May 22 07:31:03 PDT 2008


Author: mcayland
Date: 2008-05-22 07:31:02 -0700 (Thu, 22 May 2008)
New Revision: 2779

Added:
   trunk/topology/topology.sql.in.c
Removed:
   trunk/topology/topology.sql.in
Modified:
   trunk/topology/Makefile
Log:
Update topology/ directory to use the new PGXS build system.

Modified: trunk/topology/Makefile
===================================================================
--- trunk/topology/Makefile	2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/Makefile	2008-05-22 14:31:02 UTC (rev 2779)
@@ -1,27 +1,28 @@
-# Configuration Directives
-include ../Makefile.config
-include ../Version.config
+#
+# PostGIS PGXS build system
+#
 
-#---------------------------------------------------------------
-# Postgis version and build date
-#---------------------------------------------------------------
+# Files to be copied to the contrib/ directory
+DATA_built=topology.sql
 
-all: topology.sql 
+# SQL objects (files requiring C pre-processing)
+SQL_OBJS=topology.sql.in
 
-topology.sql: topology.sql.in
-	cpp -P -traditional-cpp -DUSE_VERSION=$(USE_VERSION) $< | grep -v '^#' > $@
+# Extra files to remove during 'make clean'
+EXTRA_CLEAN=$(SQL_OBJS)
 
-install: all install-topology-scripts
+# PGXS information
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
 
-uninstall: uninstall-topology-scripts
 
-install-topology-scripts:
-	@mkdir -p $(DESTDIR)$(datadir)
-	$(INSTALL_DATA) topology.sql $(DESTDIR)$(datadir)/topology.sql
+# Unfortunately we have to copy this from the PGXS Makefile as it only gets picked up
+# if MODULE_big is defined
+%.sql: %.sql.in
+	sed 's,MODULE_PATHNAME,$$libdir/$*,g' $< >$@
 
-uninstall-topology-scripts:
-	rm -f $(DESTDIR)$(datadir)/postgis/topology.sql
+# Generate any .sql.in files from .sql.in.c files by running them through the C pre-processor 
+$(SQL_OBJS): %.in: %.in.c
+	$(CPP) -traditional-cpp $< | grep -v '^#' > $@
 
-clean distclean: 
-	rm -f topology.sql
-

Deleted: trunk/topology/topology.sql.in
===================================================================
--- trunk/topology/topology.sql.in	2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/topology.sql.in	2008-05-22 14:31:02 UTC (rev 2779)
@@ -1,3827 +0,0 @@
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--- 
--- $Id$
---
--- PostGIS - Spatial Types for PostgreSQL
--- http://postgis.refractions.net
--- Copyright 2005 Refractions Research Inc.
---
--- This is free software; you can redistribute and/or modify it under
--- the terms of the GNU General Public Licence. See the COPYING file.
---
--- Author: Sandro Santilli <strk at refractions.net>
---  
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
--- STATUS:
---
---	All objects are created in the 'topology' schema.
---
---	We have PostGIS-specific objects and SQL/MM objects.
---	PostGIS-specific objects have no prefix, SQL/MM ones
---	have the ``ST_'' prefix.
---	
--- [PostGIS-specific]
---
---   TABLE topology
---	Table storing topology info (name, srid, precision)
---
---   TYPE TopoGeometry
---	Complex type storing topology_id, layer_id, geometry type
---	and topogeometry id.
---
---   DOMAIN TopoElement
---	An array of two elements: element_id and element_type.
---	In fact, an array of integers.
---
---   DOMAIN TopoElementArray
---	An array of element_id,element_type values.
---	In fact, a bidimensional array of integers:
---	'{{id,type}, {id,type}, ...}'
---
---   FUNCTION CreateTopology(name, [srid], [precision])
---	Initialize a new topology (creating schema with
---	edge,face,node,relation) and add a record into
---	the topology.topology table.
---	TODO: add triggers (or rules, or whatever) enforcing
---	      precision to the edge and node tables.
---  
---   FUNCTION DropTopology(name)
---  	Delete a topology removing reference from the
---  	topology.topology table
---
---   FUNCTION GetTopologyId(name)
---   FUNCTION GetTopologyName(id)
---	Return info about a Topology
---
---   FUNCTION AddTopoGeometryColumn(toponame, schema, table, column, geomtype)
---      Add a TopoGeometry column to a table, making it a topology layer.
---      Returns created layer id.
---
---   FUNCTION DropTopoGeometryColumn(schema, table, column)
---  	Drop a TopoGeometry column, unregister the associated layer,
--- 	cleanup the relation table.
---
---   FUNCTION CreateTopoGeom(toponame, geomtype, layer_id, topo_objects)
---  	Create a TopoGeometry object from existing Topology elements.
---	The "topo_objects" parameter is of TopoElementArray type.
---
---   FUNCTION GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
---   FUNCTION GetTopoGeomElementArray(TopoGeometry)
---  	Returns a TopoElementArray object containing the topological
---	elements of the given TopoGeometry.
---
---   FUNCTION GetTopoGeomElements(toponame, layer_id, topogeom_id)
---   FUNCTION GetTopoGeomElements(TopoGeometry)
---  	Returns a set of TopoElement objects containing the
---	topological elements of the given TopoGeometry (primitive
---	elements)
---
---   FUNCTION ValidateTopology(toponame)
---  	Run validity checks on the topology, returning, for each
---	detected error, a 3-columns row containing error string
---	and references to involved topo elements: error, id1, id2
---
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
---  Overloaded functions for TopoGeometry inputs
---
---   FUNCTION intersects(TopoGeometry, TopoGeometry)
---   FUNCTION equals(TopoGeometry, TopoGeometry)
---
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
---   FUNCTION TopoGeo_AddPoint(toponame, point)
---	Add a Point geometry to the topology
---	TODO: accept a topology/layer id
---	      rework to use existing node if existent
---
---   FUNCTION TopoGeo_AddLinestring(toponame, line)
---	Add a LineString geometry to the topology
---	TODO: accept a topology/layer id
---	      rework to use existing nodes/edges
---	      splitting them if required
---
---   FUNCTION TopoGeo_AddPolygon(toponame, polygon)
---	Add a Polygon geometry to the topology
---	TODO: implement
---
---   TYPE GetFaceEdges_ReturnType
---	Complex type used to return tuples from ST_GetFaceEdges
---
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
---
--- [SQL/MM]
---
---   ST_InitTopoGeo
---  	Done, can be modified to include explicit sequences or
---  	more constraints. Very noisy due to implicit index creations
---  	for primary keys and sequences for serial fields...
---  
---   ST_CreateTopoGeo
---  	Being working on. TODO: continue implementation
---  
---   ST_AddIsoNode
---  	Complete
---  
---   ST_RemoveIsoNode
---  	Complete
---  
---   ST_MoveIsoNode
---  	Complete
---  
---   ST_AddIsoEdge
---  	Complete
---
---   ST_RemoveIsoEdge
---	Complete, exceptions untested
---  
---   ST_ChangeEdgeGeom
---  	Complete
---
---   ST_NewEdgesSplit
---	Complete
---	this also updates the Relation table
---	TODO: add entries to the History table ?
---
---   ST_ModEdgesSplit
---	Complete
---	this also updates the Relation table
---	TODO: add entries to the History table ?
---
---   ST_AddEdgeNewFaces
---	Being working on. TODO: continue
---  
---   ST_GetFaceEdges
---  	Unimplemented (C seems appropriate)
---  
---   ST_GetFaceGeometry
---  	Implemented using polygonize()
---  
---  
-
-
-#define CREATEFUNCTION CREATE OR REPLACE FUNCTION
-
-#if USE_VERSION > 72
-# define _IMMUTABLE_STRICT IMMUTABLE STRICT
-# define _IMMUTABLE IMMUTABLE
-# define _STABLE_STRICT STABLE STRICT
-# define _STABLE STABLE
-# define _VOLATILE_STRICT VOLATILE STRICT
-# define _VOLATILE VOLATILE
-# define _STRICT STRICT
-#else 
-# define _IMMUTABLE_STRICT  with(iscachable,isstrict)
-# define _IMMUTABLE with(iscachable)
-# define _STABLE_STRICT with(isstrict)
-# define _STABLE 
-# define _VOLATILE_STRICT with(isstrict)
-# define _VOLATILE 
-# define _STRICT with(isstrict)
-#endif 
-
-DROP SCHEMA topology CASCADE;
-
-BEGIN;
-
-CREATE SCHEMA topology;
-
---={ ----------------------------------------------------------------
---  POSTGIS-SPECIFIC block
---
---  This part contains function NOT in the SQL/MM specification
---
----------------------------------------------------------------------
-
---
--- Topology table.
--- Stores id,name,precision and SRID of topologies.
---
-CREATE TABLE topology.topology (
-	id SERIAL NOT NULL PRIMARY KEY,
-	name VARCHAR NOT NULL UNIQUE,
-	SRID INTEGER NOT NULL,
-	precision FLOAT8 NOT NULL
-);
-
---{ LayerTrigger()
---
--- Layer integrity trigger
---
-CREATEFUNCTION topology.LayerTrigger()
-	RETURNS trigger
-AS
-'
-DECLARE
-	rec RECORD;
-	ok BOOL;
-	toponame varchar;
-	query TEXT;
-BEGIN
-
-	--RAISE NOTICE ''LayerTrigger called % % at % level'', TG_WHEN, TG_OP, TG_LEVEL;
-
-
-	IF TG_OP = ''INSERT'' THEN
-		RAISE EXCEPTION ''LayerTrigger not meant to be called on INSERT'';
-	ELSIF TG_OP = ''UPDATE'' THEN
-		RAISE EXCEPTION ''The topology.layer table cannot be updated'';
-	END IF;
-
-
-	-- Check for existance of any feature column referencing
-	-- this layer
-	FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
-		WHERE text(n.nspname) = OLD.schema_name
-		AND c.relnamespace = n.oid
-		AND text(c.relname) = OLD.table_name
-		AND a.attrelid = c.oid
-		AND text(a.attname) = OLD.feature_column
-	LOOP
-		query = ''SELECT * ''
-			|| '' FROM '' || quote_ident(OLD.schema_name)
-			|| ''.'' || quote_ident(OLD.table_name)
-			|| '' WHERE layer_id(''
-			|| quote_ident(OLD.feature_column)||'') ''
-			|| ''='' || OLD.layer_id
-			|| '' LIMIT 1'';
-		--RAISE NOTICE ''%'', query;
-		FOR rec IN EXECUTE query
-		LOOP
-			RAISE NOTICE ''A feature referencing layer % of topology % still exists in %.%.%'', OLD.layer_id, OLD.topology_id, OLD.schema_name, OLD.table_name, OLD.feature_column;
-			RETURN NULL;
-		END LOOP;
-	END LOOP;
-
-
-	-- Get topology name
-	SELECT name FROM topology.topology INTO toponame
-		WHERE id = OLD.topology_id;
-
-	IF toponame IS NULL THEN
-		RAISE NOTICE ''Could not find name of topology with id %'',
-			OLD.layer_id;
-	END IF;
-
-	-- Check if any record in the relation table references this layer
-	FOR rec IN SELECT * FROM pg_namespace
-		WHERE text(nspname) = toponame
-	LOOP
-		query = ''SELECT * ''
-			|| '' FROM '' || quote_ident(toponame)
-			|| ''.relation ''
-			|| '' WHERE topogeo_id = '' || OLD.topology_id 
-			|| '' AND layer_id = ''|| OLD.layer_id
-			|| '' LIMIT 1'';
-		--RAISE NOTICE ''%'', query;
-		FOR rec IN EXECUTE query
-		LOOP
-			RAISE NOTICE ''A record in %.relation still references layer %'', toponame, OLD.layer_id;
-			RETURN NULL;
-		END LOOP;
-	END LOOP;
-
-	RETURN OLD;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} LayerTrigger()
-
-
---{
--- Layer table.
--- Stores topology layer informations
---
-CREATE TABLE topology.layer (
-	topology_id INTEGER NOT NULL
-		REFERENCES topology.topology(id),
-	layer_id integer NOT NULL,
-	schema_name VARCHAR NOT NULL,
-	table_name VARCHAR NOT NULL,
-	feature_column VARCHAR NOT NULL,
-	feature_type VARCHAR NOT NULL,
-	level INTEGER NOT NULL DEFAULT 0,
-	child_id INTEGER DEFAULT NULL,
-	UNIQUE(schema_name, table_name, feature_column),
-	PRIMARY KEY(topology_id, layer_id)
-);
-
-CREATE TRIGGER layer_integrity_checks BEFORE UPDATE OR DELETE
-ON topology.layer FOR EACH ROW EXECUTE PROCEDURE topology.LayerTrigger();
-
---} Layer table.
-
---
--- Type returned by ST_GetFaceEdges
---
-CREATE TYPE topology.GetFaceEdges_ReturnType AS (
-	sequence integer,
-	edge integer
-);
-
---
--- Type returned by ValidateTopology 
---
-CREATE TYPE topology.ValidateTopology_ReturnType AS (
-	error varchar,
-	id1 integer,
-	id2 integer
-);
-
---
--- TopoGeometry type
---
-CREATE TYPE topology.TopoGeometry AS (
-	topology_id integer,
-	layer_id integer,
-	id integer,
-	type integer -- 1: [multi]point, 2: [multi]line,
-	             -- 3: [multi]polygon, 4: collection
-);
-
---
--- TopoElement domain
---
-CREATE DOMAIN topology.TopoElement AS integer[]
-	CONSTRAINT DIMENSIONS CHECK (
-		array_upper(VALUE, 2) IS NULL
-		AND array_upper(VALUE, 1) = 2
-	);
---
--- TopoElementArray domain
---
-CREATE DOMAIN topology.TopoElementArray AS integer[][]
-	CONSTRAINT DIMENSIONS CHECK (
-		array_upper(VALUE, 2) IS NOT NULL
-		AND array_upper(VALUE, 2) = 2
-		AND array_upper(VALUE, 3) IS NULL
-	);
-
---
--- TopoGeomElementArray domain
---
-CREATE DOMAIN topology.TopoGeomElementArray AS integer[][]
-	CONSTRAINT DIMENSIONS CHECK (
-		array_upper(VALUE, 2) IS NOT NULL
-		AND array_upper(VALUE, 2) = 2
-		AND array_upper(VALUE, 3) IS NULL
-	);
-
---{ RelationTrigger()
---
--- Relation integrity trigger
---
-CREATEFUNCTION topology.RelationTrigger()
-	RETURNS trigger
-AS
-'
-DECLARE
-	toponame varchar;
-	topoid integer;
-	plyr RECORD; -- parent layer
-	rec RECORD;
-	ok BOOL;
-
-BEGIN
-	IF TG_NARGS != 2 THEN
-		RAISE EXCEPTION ''RelationTrigger called with wrong number of arguments'';
-	END IF;
-
-	topoid = TG_ARGV[0];
-	toponame = TG_ARGV[1];
-
-	--RAISE NOTICE ''RelationTrigger called % % on %.relation for a %'', TG_WHEN, TG_OP, toponame, TG_LEVEL;
-
-
-	IF TG_OP = ''DELETE'' THEN
-		RAISE EXCEPTION ''RelationTrigger not meant to be called on DELETE'';
-	END IF;
-
-	-- Get layer info (and verify it exists)
-	ok = false;
-	FOR plyr IN EXECUTE ''SELECT * FROM topology.layer ''
-		|| ''WHERE ''
-		|| '' topology_id = '' || topoid
-		|| '' AND''
-		|| '' layer_id = '' || NEW.layer_id
-	LOOP
-		ok = true;
-		EXIT;
-	END LOOP;
-	IF NOT ok THEN
-		RAISE EXCEPTION ''Layer % does not exist in topology %'',
-			NEW.layer_id, topoid;
-		RETURN NULL;
-	END IF;
-
-	IF plyr.level > 0 THEN -- this is hierarchical layer
-
-		-- ElementType must be the layer child id
-		IF NEW.element_type != plyr.child_id THEN
-			RAISE EXCEPTION ''Type of elements in layer % must be set to its child layer id %'', plyr.layer_id, plyr.child_id;
-			RETURN NULL;
-		END IF;
-
-		-- ElementId must be an existent TopoGeometry in child layer
-		ok = false;
-		FOR rec IN EXECUTE ''SELECT topogeo_id FROM ''
-			|| quote_ident(toponame) || ''.relation ''
-			|| '' WHERE layer_id = '' || plyr.child_id 
-			|| '' AND topogeo_id = '' || NEW.element_id
-		LOOP
-			ok = true;
-			EXIT;
-		END LOOP;
-		IF NOT ok THEN
-			RAISE EXCEPTION ''TopoGeometry % does not exist in the child layer %'', NEW.element_id, plyr.child_id;
-			RETURN NULL;
-		END IF;
-
-	ELSE -- this is a basic layer
-
-		-- ElementType must be compatible with layer type
-		IF plyr.feature_type != 4
-			AND plyr.feature_type != NEW.element_type
-		THEN
-			RAISE EXCEPTION ''Element of type % is not compatible with layer of type %'', NEW.element_type, plyr.feature_type;
-			RETURN NULL;
-		END IF;
-
-		--
-		-- Now lets see if the element is consistent, which
-		-- is it exists in the topology tables.
-		--
-
-		--
-		-- Element is a Node
-		--
-		IF NEW.element_type = 1 
-		THEN
-			ok = false;
-			FOR rec IN EXECUTE ''SELECT node_id FROM ''
-				|| quote_ident(toponame) || ''.node ''
-				|| '' WHERE node_id = '' || NEW.element_id
-			LOOP
-				ok = true;
-				EXIT;
-			END LOOP;
-			IF NOT ok THEN
-				RAISE EXCEPTION ''Node % does not exist in topology %'', NEW.element_id, toponame;
-				RETURN NULL;
-			END IF;
-
-		--
-		-- Element is an Edge
-		--
-		ELSIF NEW.element_type = 2 
-		THEN
-			ok = false;
-			FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-				|| quote_ident(toponame) || ''.edge_data ''
-				|| '' WHERE edge_id = '' || abs(NEW.element_id)
-			LOOP
-				ok = true;
-				EXIT;
-			END LOOP;
-			IF NOT ok THEN
-				RAISE EXCEPTION ''Edge % does not exist in topology %'', NEW.element_id, toponame;
-				RETURN NULL;
-			END IF;
-
-		--
-		-- Element is a Face
-		--
-		ELSIF NEW.element_type = 3 
-		THEN
-			IF NEW.element_id = 0 THEN
-				RAISE EXCEPTION ''Face % cannot be associated with any feature'', NEW.element_id;
-				RETURN NULL;
-			END IF;
-			ok = false;
-			FOR rec IN EXECUTE ''SELECT face_id FROM ''
-				|| quote_ident(toponame) || ''.face ''
-				|| '' WHERE face_id = '' || NEW.element_id
-			LOOP
-				ok = true;
-				EXIT;
-			END LOOP;
-			IF NOT ok THEN
-				RAISE EXCEPTION ''Face % does not exist in topology %'', NEW.element_id, toponame;
-				RETURN NULL;
-			END IF;
-		END IF;
-
-	END IF;
-	
-	RETURN NEW;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} RelationTrigger()
-
---{
---  AddTopoGeometryColumn(toponame, schema, table, colum, type, [child])
---
---  Add a TopoGeometry column to a table, making it a topology layer.
---  Returns created layer id.
---
---
-CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar, integer)
-	RETURNS integer
-AS '
-DECLARE
-	toponame alias for $1;
-	schema alias for $2;
-	table alias for $3;
-	col alias for $4;
-	ltype alias for $5;
-	child alias for $6;
-	intltype integer;
-	level integer;
-	topoid integer;
-	rec RECORD;
-	layer_id integer;
-	query text;
-BEGIN
-
-        -- Get topology id
-        SELECT id FROM topology.topology into topoid
-                WHERE name = toponame;
-
-	IF topoid IS NULL THEN
-		RAISE EXCEPTION ''Topology % does not exist'', toponame;
-	END IF;
-
-	--
-	-- Get new layer id from sequence
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		quote_ident(toponame) || ''.layer_id_seq'''')''
-	LOOP
-		layer_id = rec.nextval;
-	END LOOP;
-
-	IF ltype = ''POINT'' THEN
-		intltype = 1;
-	ELSIF ltype = ''LINE'' THEN
-		intltype = 2;
-	ELSIF ltype = ''POLYGON'' THEN
-		intltype = 3;
-	ELSIF ltype = ''COLLECTION'' THEN
-		intltype = 4;
-	ELSE
-		RAISE EXCEPTION ''Layer type must be one of POINT,LINE,POLYGON,COLLECTION'';
-	END IF;
-
-	--
-	-- See if child id exists and extract its level
-	--
-	IF child IS NULL THEN
-		EXECUTE ''INSERT INTO ''
-			|| ''topology.layer(topology_id, ''
-			|| ''layer_id, schema_name, ''
-			|| ''table_name, feature_column, feature_type) ''
-			|| ''VALUES (''
-			|| topoid || '',''
-			|| layer_id || '','' 
-			|| quote_literal(schema) || '',''
-			|| quote_literal(table) || '',''
-			|| quote_literal(col) || '',''
-			|| intltype || '');'';
-	ELSE
-		FOR rec IN EXECUTE ''SELECT level FROM topology.layer''
-			|| '' WHERE layer_id = '' || child
-		LOOP
-			level = rec.level + 1;
-		END LOOP;
-
-		EXECUTE ''INSERT INTO '' 
-			|| ''topology.layer(topology_id, ''
-			|| ''layer_id, level, child_id, schema_name, ''
-			|| ''table_name, feature_column, feature_type) ''
-			|| ''VALUES (''
-			|| topoid || '',''
-			|| layer_id || '','' || level || '',''
-			|| child || '',''
-			|| quote_literal(schema) || '',''
-			|| quote_literal(table) || '',''
-			|| quote_literal(col) || '',''
-			|| intltype || '');'';
-	END IF;
-
-
-	--
-	-- Create a sequence for TopoGeometries in this new layer
-	--
-	EXECUTE ''CREATE SEQUENCE '' || quote_ident(toponame)
-		|| ''.topogeo_s_'' || layer_id;
-
-	--
-	-- Add new TopoGeometry column in schema.table
-	--
-	EXECUTE ''ALTER TABLE '' || quote_ident(schema)
-		|| ''.'' || quote_ident(table) 
-		|| '' ADD COLUMN '' || quote_ident(col)
-		|| '' topology.TopoGeometry;'';
-
-	--
-	-- Add constraints on TopoGeom column
-	--
-	EXECUTE ''ALTER TABLE '' || quote_ident(schema)
-		|| ''.'' || quote_ident(table) 
-		|| '' ADD CONSTRAINT check_topogeom CHECK (''
-		|| ''topology_id('' || quote_ident(col) || '') = '' || topoid
-		|| '' AND ''
-		|| ''layer_id('' || quote_ident(col) || '') = '' || layer_id
-		|| '' AND ''
-		|| ''type('' || quote_ident(col) || '') = '' || intltype
-		|| '');'';
-
-	--
-	-- Add dependency of the feature column on the topology schema
-	--
-	query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
-		|| ''fcat.oid, fobj.oid, fsub.attnum, tcat.oid, ''
-		|| ''tobj.oid, 0, ''''n'''' ''
-		|| ''FROM pg_class fcat, pg_namespace fnsp, ''
-		|| '' pg_class fobj, pg_attribute fsub, ''
-		|| '' pg_class tcat, pg_namespace tobj ''
-		|| '' WHERE fcat.relname = ''''pg_class'''' ''
-		|| '' AND fnsp.nspname = '' || quote_literal(schema)
-		|| '' AND fobj.relnamespace = fnsp.oid ''
-		|| '' AND fobj.relname = '' || quote_literal(table)
-		|| '' AND fsub.attrelid = fobj.oid ''
-		|| '' AND fsub.attname = '' || quote_literal(col)
-		|| '' AND tcat.relname = ''''pg_namespace'''' ''
-		|| '' AND tobj.nspname = '' || quote_literal(toponame);
-
---
--- The only reason to add this dependency is to avoid
--- simple drop of a feature column. Still, drop cascade
--- will remove both the feature column and the sequence
--- corrupting the topology anyway ...
---
-#if 0
-	--
-	-- Add dependency of the topogeom sequence on the feature column 
-	-- This is a dirty hack ...
-	--
-	query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
-		|| ''scat.oid, sobj.oid, 0, fcat.oid, ''
-		|| ''fobj.oid, fsub.attnum, ''''n'''' ''
-		|| ''FROM pg_class fcat, pg_namespace fnsp, ''
-		|| '' pg_class fobj, pg_attribute fsub, ''
-		|| '' pg_class scat, pg_class sobj, ''
-		|| '' pg_namespace snsp ''
-		|| '' WHERE fcat.relname = ''''pg_class'''' ''
-		|| '' AND fnsp.nspname = '' || quote_literal(schema)
-		|| '' AND fobj.relnamespace = fnsp.oid ''
-		|| '' AND fobj.relname = '' || quote_literal(table)
-		|| '' AND fsub.attrelid = fobj.oid ''
-		|| '' AND fsub.attname = '' || quote_literal(col)
-		|| '' AND scat.relname = ''''pg_class'''' ''
-		|| '' AND snsp.nspname = '' || quote_literal(toponame)
-		|| '' AND sobj.relnamespace = snsp.oid '' 
-		|| '' AND sobj.relname = ''
-		|| '' ''''topogeo_s_'' || layer_id || '''''' '';
-
-	RAISE NOTICE ''%'', query;
-	EXECUTE query;
-#endif
-
-	RETURN layer_id;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE;
-
-CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar)
-	RETURNS integer
-AS '
-	SELECT topology.AddTopoGeometryColumn($1, $2, $3, $4, $5, NULL);
-'
-LANGUAGE 'sql' _VOLATILE;
-
---
---} AddTopoGeometryColumn
-
---{
---  DropTopoGeometryColumn(schema, table, colum)
---
---  Drop a TopoGeometry column, unregister the associated layer,
---  cleanup the relation table.
---
---
-CREATEFUNCTION topology.DropTopoGeometryColumn(varchar, varchar, varchar)
-	RETURNS text
-AS '
-DECLARE
-	schema alias for $1;
-	table alias for $2;
-	col alias for $3;
-	rec RECORD;
-	lyrinfo RECORD;
-	ok BOOL;
-	result text;
-BEGIN
-
-        -- Get layer and topology info
-	ok = false;
-	FOR rec IN EXECUTE ''SELECT t.name as toponame, l.* FROM ''
-		|| ''topology.topology t, topology.layer l ''
-		|| '' WHERE l.schema_name = '' || quote_literal(schema)
-		|| '' AND l.table_name = '' || quote_literal(table)
-		|| '' AND l.feature_column = '' || quote_literal(col)
-	LOOP
-		ok = true;
-		lyrinfo = rec;
-	END LOOP;
-
-	-- Layer not found
-	IF NOT ok THEN
-		RAISE EXCEPTION ''No layer registered on %.%.%'',
-			schema,table,col;
-	END IF;
-		
-	-- Clean up the topology schema
-	FOR rec IN SELECT * FROM pg_namespace
-		WHERE text(nspname) = lyrinfo.toponame
-	LOOP
-		-- Cleanup the relation table
-		EXECUTE ''DELETE FROM '' || quote_ident(lyrinfo.toponame)
-			|| ''.relation ''
-			|| '' WHERE ''
-			|| ''layer_id = '' || lyrinfo.layer_id;
-
-		-- Drop the sequence for topogeoms in this layer
-		EXECUTE ''DROP SEQUENCE '' || quote_ident(lyrinfo.toponame)
-			|| ''.topogeo_s_'' || lyrinfo.layer_id;
-
-	END LOOP;
-
-	ok = false;
-	FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
-		WHERE text(n.nspname) = schema
-		AND c.relnamespace = n.oid
-		AND text(c.relname) = table
-		AND a.attrelid = c.oid
-		AND text(a.attname) = col
-	LOOP
-		ok = true;
-		EXIT;
-	END LOOP;
-
-
-	IF ok THEN
-		-- Set feature column to NULL to bypass referential integrity
-		-- checks
-		EXECUTE ''UPDATE '' || quote_ident(schema) || ''.''
-			|| quote_ident(table)
-			|| '' SET '' || quote_ident(col)
-			|| '' = NULL'';
-	END IF;
-
-	-- Delete the layer record
-	EXECUTE ''DELETE FROM topology.layer ''
-		|| '' WHERE topology_id = '' || lyrinfo.topology_id
-		|| '' AND layer_id = '' || lyrinfo.layer_id;
-
-	IF ok THEN
-		-- Drop the layer column
-		EXECUTE ''ALTER TABLE '' || quote_ident(schema) || ''.''
-			|| quote_ident(table)
-			|| '' DROP '' || quote_ident(col)
-			|| '' cascade'';
-	END IF;
-
-	result = ''Layer '' || lyrinfo.layer_id || '' (''
-		|| schema || ''.'' || table || ''.'' || col
-		|| '') dropped'';
-
-	RETURN result;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---
---} DropTopoGeometryColumn
-
-
---{
--- CreateTopoGeom(topology_name, topogeom_type, layer_id, elements)
---
--- Create a TopoGeometry object from Topology elements.
--- The elements parameter is a two-dimensional array. 
--- Every element of the array is either a Topology element represented by
--- (id, type) or a TopoGeometry element represented by (id, layer).
--- The actual semantic depends on the TopoGeometry layer, either at
--- level 0 (elements are topological primitives) or higer (elements
--- are TopoGeoms from child layer).
--- 
--- Return a topology.TopoGeometry object.
---
-CREATEFUNCTION topology.CreateTopoGeom(varchar, integer, integer, topology.TopoElementArray)
-	RETURNS topology.TopoGeometry
-AS '
-DECLARE
-	toponame alias for $1;
-	tg_type alias for $2; -- 1:[multi]point
-	                      -- 2:[multi]line
-	                      -- 3:[multi]poly
-	                      -- 4:collection
-	layer_id alias for $3;
-	tg_objs alias for $4;
-	i integer;
-	dims varchar;
-	outerdims varchar;
-	innerdims varchar;
-	obj_type integer;
-	obj_id integer;
-	ret topology.TopoGeometry;
-	rec RECORD;
-	layertype integer;
-	layerlevel integer;
-	layerchild integer;
-BEGIN
-
-	IF tg_type < 1 OR tg_type > 4 THEN
-		RAISE EXCEPTION ''Invalid TopoGeometry type (must be in the range 1..4'';
-	END IF;
-
-	-- Get topology id into return TopoGeometry
-	SELECT id FROM topology.topology into ret.topology_id
-		WHERE name = toponame;
-
-	--
-	-- Get layer info
-	--
-	layertype := NULL;
-	FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
-		|| '' WHERE topology_id = '' || ret.topology_id
-		|| '' AND layer_id = '' || layer_id
-	LOOP
-		layertype = rec.feature_type;
-		layerlevel = rec.level;
-		layerchild = rec.child_id;
-	END LOOP;
-
-	-- Check for existence of given layer id
-	IF layertype IS NULL THEN
-		RAISE EXCEPTION ''No layer with id % is registered with topology %'', layer_id, toponame;
-	END IF;
-
-	-- Verify compatibility between layer geometry type and
-	-- TopoGeom requested geometry type
-	IF layertype != 4 and layertype != tg_type THEN
-		RAISE EXCEPTION ''A Layer of type % cannot contain a TopoGeometry of type %'', layertype, tg_type;
-	END IF;
-
-	-- Set layer id and type in return object
-	ret.layer_id = layer_id;
-	ret.type = tg_type;
-
-	--
-	-- Get new TopoGeo id from sequence
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		toponame || ''.topogeo_s_'' || layer_id || '''''')''
-	LOOP
-		ret.id = rec.nextval;
-	END LOOP;
-
-	-- Loop over outer dimension
-	i = array_lower(tg_objs, 1);
-	LOOP
-		obj_id = tg_objs[i][1];
-		obj_type = tg_objs[i][2];
-		IF layerlevel = 0 THEN -- array specifies lower-level objects
-			IF tg_type != 4 and tg_type != obj_type THEN
-				RAISE EXCEPTION ''A TopoGeometry of type % cannot contain topology elements of type %'', tg_type, obj_type;
-			END IF;
-		ELSE -- array specifies lower-level topogeometries
-			IF obj_type != layerchild THEN
-				RAISE EXCEPTION ''TopoGeom element layer do not match TopoGeom child layer'';
-			END IF;
-			-- TODO: verify that the referred TopoGeometry really
-			-- exists in the relation table ?
-		END IF;
-
-		--RAISE NOTICE ''obj:% type:% id:%'', i, obj_type, obj_id;
-
-		--
-		-- Insert record into the Relation table
-		--
-		EXECUTE ''INSERT INTO ''||quote_ident(toponame)
-			|| ''.relation(topogeo_id, layer_id, ''
-			|| ''element_id,element_type) ''
-			|| '' VALUES (''||ret.id
-			||'',''||ret.layer_id
-			|| '','' || obj_id || '','' || obj_type || '');'';
-
-		i = i+1;
-		IF i > array_upper(tg_objs, 1) THEN
-			EXIT;
-		END IF;
-	END LOOP;
-
-	RETURN ret;
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} CreateTopoGeom(toponame,topogeom_type, TopoObject[])
-
---{
--- GetTopologyName(topology_id)
---
-CREATEFUNCTION topology.GetTopologyName(integer)
-	RETURNS varchar
-AS
-'
-DECLARE
-	topoid alias for $1;
-	ret varchar;
-BEGIN
-        SELECT name FROM topology.topology into ret
-                WHERE id = topoid;
-	RETURN ret;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} GetTopologyName(topoid)
-
---{
--- GetTopologyId(toponame)
---
-CREATEFUNCTION topology.GetTopologyId(varchar)
-	RETURNS integer
-AS
-'
-DECLARE
-	toponame alias for $1;
-	ret integer;
-BEGIN
-        SELECT id FROM topology.topology into ret
-                WHERE name = toponame;
-	RETURN ret;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} GetTopologyId(toponame)
-	
-
-
---{
--- GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
--- GetTopoGeomElementArray(TopoGeometry)
---
--- Returns a set of element_id,element_type
---
-CREATEFUNCTION topology.GetTopoGeomElementArray(varchar, integer, integer)
-	RETURNS topology.TopoElementArray
-AS
-'
-DECLARE
-	toponame alias for $1;
-	layerid alias for $2;
-	tgid alias for $3;
-	rec RECORD;
-	tg_objs varchar := ''{'';
-	i integer;
-	query text;
-BEGIN
-
-	query = ''SELECT * FROM topology.GetTopoGeomElements(''
-		|| quote_literal(toponame) || '',''
-		|| quote_literal(layerid) || '',''
-		|| quote_literal(tgid)
-		|| '') as obj ORDER BY obj'';
-
-	RAISE NOTICE ''Query: %'', query;
-
-	i = 1;
-	FOR rec IN EXECUTE query
-	LOOP
-		IF i > 1 THEN
-			tg_objs = tg_objs || '','';
-		END IF;
-		tg_objs = tg_objs || ''{''
-			|| rec.obj[1] || '','' || rec.obj[2]
-			|| ''}'';
-		i = i+1;
-	END LOOP;
-
-	tg_objs = tg_objs || ''}'';
-
-	RETURN tg_objs;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.GetTopoGeomElementArray(topology.TopoGeometry)
-	RETURNS topology.TopoElementArray
-AS
-'
-DECLARE
-	tg alias for $1;
-	toponame varchar;
-	ret topology.TopoElementArray;
-BEGIN
-	toponame = topology.GetTopologyName(tg.topology_id);
-	ret = topology.GetTopoGeomElementArray(toponame,tg.layer_id,tg.id);
-	RETURN ret;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
---} GetTopoGeomElementArray()
-
---{
--- GetTopoGeomElements(toponame, layer_id, topogeom_id)
--- GetTopoGeomElements(TopoGeometry)
---
--- Returns a set of element_id,element_type
---
-CREATEFUNCTION topology.GetTopoGeomElements(varchar, integer, integer)
-	RETURNS SETOF topology.TopoElement
-AS
-'
-DECLARE
-	toponame alias for $1;
-	layerid alias for $2;
-	tgid alias for $3;
-	ret topology.TopoElement;
-	rec RECORD;
-	rec2 RECORD;
-	query text;
-	query2 text;
-	lyr RECORD;
-	ok bool;
-BEGIN
-
-	-- Get layer info
-	ok = false;
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| '' topology.layer ''
-		|| '' WHERE layer_id = '' || layerid
-	LOOP
-		lyr = rec;
-		ok = true;
-	END LOOP;
-
-	IF NOT ok THEN
-		RAISE EXCEPTION ''Layer % does not exist'', layerid;
-	END IF;
-
-
-	query = ''SELECT abs(element_id) as element_id, element_type FROM ''
-		|| quote_ident(toponame) || ''.relation WHERE ''
-		|| '' layer_id = '' || layerid
-		|| '' AND topogeo_id = '' || quote_literal(tgid)
-		|| '' ORDER BY element_type, element_id'';
-
-	--RAISE NOTICE ''Query: %'', query;
-
-	FOR rec IN EXECUTE query
-	LOOP
-		IF lyr.level > 0 THEN
-			query2 = ''SELECT * from topology.GetTopoGeomElements(''
-				|| quote_literal(toponame) || '',''
-				|| rec.element_type
-				|| '',''
-				|| rec.element_id
-				|| '') as ret;'';
-			--RAISE NOTICE ''Query2: %'', query2;
-			FOR rec2 IN EXECUTE query2
-			LOOP
-				RETURN NEXT rec2.ret;
-			END LOOP;
-		ELSE
-			ret = ''{'' || rec.element_id || '','' || rec.element_type || ''}'';
-			RETURN NEXT ret;
-		END IF;
-	
-	END LOOP;
-
-	RETURN;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.GetTopoGeomElements(topology.TopoGeometry)
-	RETURNS SETOF topology.TopoElement
-AS
-'
-DECLARE
-	tg alias for $1;
-	toponame varchar;
-	rec RECORD;
-BEGIN
-	toponame = topology.GetTopologyName(tg.topology_id);
-	FOR rec IN SELECT * FROM topology.GetTopoGeomElements(toponame,
-		tg.layer_id,tg.id) as ret
-	LOOP
-		RETURN NEXT rec.ret;
-	END LOOP;
-	RETURN;
-END;
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
---} GetTopoGeomElements()
-
---{
--- Geometry(TopoGeometry)
---
--- Construct a Geometry from a TopoGeometry.
--- 
---
-CREATE OR REPLACE FUNCTION topology.Geometry(topology.TopoGeometry)
-	RETURNS Geometry
-AS '
-DECLARE
-	topogeom alias for $1;
-	toponame varchar;
-	geom geometry;
-	rec RECORD;
-	plyr RECORD;
-	clyr RECORD;
-	query text;
-	ok BOOL;
-BEGIN
-        -- Get topology name
-        SELECT name FROM topology.topology into toponame
-                WHERE id = topogeom.topology_id;
-
-	-- Get layer info
-	ok = false;
-	FOR rec IN EXECUTE ''SELECT * FROM topology.layer ''
-		|| '' WHERE topology_id = '' || topogeom.topology_id
-		|| '' AND layer_id = '' || topogeom.layer_id
-	LOOP
-		ok = true;
-		plyr = rec;
-	END LOOP;
-
-	IF NOT ok THEN
-		RAISE EXCEPTION ''Could not find TopoGeometry layer % in topology %'', topogeom.layer_id, topogeom.topology_id;
-	END IF;
-
-	--
-	-- If this feature layer is on any level > 0 we will
-	-- compute the topological union of all child features
-	-- in fact recursing.
-	--
-	IF plyr.level > 0 THEN
-		-- Get child layer info
-		FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
-			|| '' WHERE layer_id = '' || plyr.child_id
-		LOOP
-			clyr = rec;
-		END LOOP;
-
-		query = ''SELECT geomunion(topology.Geometry(''
-			|| quote_ident(clyr.feature_column)
-			|| '')) as geom FROM ''
-			|| quote_ident(clyr.schema_name) || ''.''
-			|| quote_ident(clyr.table_name)
-			|| '', '' || quote_ident(toponame) || ''.relation pr''
-			|| '' WHERE ''
-			|| '' pr.topogeo_id = '' || topogeom.id
-			|| '' AND ''
-			|| '' pr.layer_id = '' || topogeom.layer_id
-			|| '' AND ''
-			|| '' id(''||quote_ident(clyr.feature_column)
-			|| '') = pr.element_id ''
-			|| '' AND ''
-			|| ''layer_id(''||quote_ident(clyr.feature_column)
-			|| '') = pr.element_type '';
-		--RAISE NOTICE ''%'', query;
-		FOR rec IN EXECUTE query
-		LOOP
-			RETURN rec.geom;
-		END LOOP;
-			
-	END IF;
-	
-
-	IF topogeom.type = 3 THEN -- [multi]polygon
-		FOR rec IN EXECUTE ''SELECT geomunion(''
-			|| ''topology.ST_GetFaceGeometry(''
-			|| quote_literal(toponame) || '',''
-			|| ''element_id)) as g FROM '' 
-			|| quote_ident(toponame)
-			|| ''.relation WHERE topogeo_id = ''
-			|| topogeom.id || '' AND layer_id = ''
-			|| topogeom.layer_id || '' AND element_type = 3 ''
-		LOOP
-			geom := rec.g;
-		END LOOP;
-
-	ELSIF topogeom.type = 2 THEN -- [multi]line
-		FOR rec IN EXECUTE ''SELECT linemerge(collect(e.geom)) as g FROM ''
-			|| quote_ident(toponame) || ''.edge e, ''
-			|| quote_ident(toponame) || ''.relation r ''
-			|| '' WHERE r.topogeo_id = '' || topogeom.id
-			|| '' AND r.layer_id = '' || topogeom.layer_id
-			|| '' AND r.element_type = 2 ''
-			|| '' AND abs(r.element_id) = e.edge_id''
-		LOOP
-			geom := rec.g;
-		END LOOP;
-	
-	ELSIF topogeom.type = 1 THEN -- [multi]point
-		FOR rec IN EXECUTE ''SELECT geomunion(n.geom) as g FROM ''
-			|| quote_ident(toponame) || ''.node n, ''
-			|| quote_ident(toponame) || ''.relation r ''
-			|| '' WHERE r.topogeo_id = '' || topogeom.id
-			|| '' AND r.layer_id = '' || topogeom.layer_id
-			|| '' AND r.element_type = 1 ''
-			|| '' AND r.element_id = n.node_id''
-		LOOP
-			geom := rec.g;
-		END LOOP;
-
-	ELSE
-		RAISE NOTICE ''Geometry from TopoGeometry does not support TopoGeometries of type % so far'', topogeom.type;
-		geom := ''GEOMETRYCOLLECTION EMPTY'';
-	END IF;
-
-	RETURN geom;
-END
-'
-LANGUAGE 'plpgsql' VOLATILE STRICT;
---} Geometry(TopoGeometry)
-
---{
---  ValidateTopology(toponame)
---
---  Return a Set of ValidateTopology_ReturnType containing
---  informations on all topology inconsistencies
---
-CREATEFUNCTION topology.ValidateTopology(varchar)
-	RETURNS setof topology.ValidateTopology_ReturnType
-AS
-'
-DECLARE
-	toponame alias for $1;
-	retrec topology.ValidateTopology_ReturnType;
-	rec RECORD;
-	i integer;
-BEGIN
-
-	-- Check for coincident nodes
-	FOR rec IN EXECUTE ''SELECT a.node_id as id1, b.node_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.node a, ''
-		|| quote_ident(toponame) || ''.node b ''
-		|| ''WHERE a.node_id < b.node_id AND a.geom && b.geom''
-	LOOP
-		retrec.error = ''coincident nodes'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for edge crossed nodes
-	FOR rec IN EXECUTE ''SELECT n.node_id as id1, e.edge_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.node n, ''
-		|| quote_ident(toponame) || ''.edge e ''
-		|| ''WHERE e.start_node != n.node_id ''
-		|| ''AND e.end_node != n.node_id ''
-		|| ''AND n.geom && e.geom ''
-		|| ''AND intersects(n.geom, e.geom)''
-	LOOP
-		retrec.error = ''edge crosses node'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for non-simple edges
-	FOR rec IN EXECUTE ''SELECT e.edge_id as id1 FROM ''
-		|| quote_ident(toponame) || ''.edge e ''
-		|| ''WHERE not issimple(e.geom)''
-	LOOP
-		retrec.error = ''edge not simple'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = NULL;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for edge crossing
-	FOR rec IN EXECUTE ''SELECT e1.edge_id as id1, e2.edge_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.edge e1, ''
-		|| quote_ident(toponame) || ''.edge e2 ''
-		|| ''WHERE e1.edge_id < e2.edge_id ''
-		|| ''AND e1.geom && e2.geom ''
-		|| ''AND crosses(e1.geom, e2.geom)''
-	LOOP
-		retrec.error = ''edge crosses edge'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for edge start_node geometry mis-match
-	FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.edge e, ''
-		|| quote_ident(toponame) || ''.node n ''
-		|| ''WHERE e.start_node = n.node_id ''
-		|| ''AND NOT Equals(StartPoint(e.geom), n.geom)''
-	LOOP
-		retrec.error = ''edge start node geometry mis-match'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for edge end_node geometry mis-match
-	FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.edge e, ''
-		|| quote_ident(toponame) || ''.node n ''
-		|| ''WHERE e.end_node = n.node_id ''
-		|| ''AND NOT Equals(EndPoint(e.geom), n.geom)''
-	LOOP
-		retrec.error = ''edge end node geometry mis-match'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for faces w/out edges
-	FOR rec IN EXECUTE ''SELECT face_id as id1 FROM ''
-		|| quote_ident(toponame) || ''.face ''
-		|| ''EXCEPT ( SELECT left_face FROM ''
-		|| quote_ident(toponame) || ''.edge ''
-		|| '' UNION SELECT right_face FROM ''
-		|| quote_ident(toponame) || ''.edge ''
-		|| '')''
-	LOOP
-		retrec.error = ''face without edges'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = NULL;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for overlapping faces 
-	-- TODO: this check requires some thinking, do we really
-	--       have to construct face geometry to detect overlap with
-	--       another face ??
-	FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.face f1, ''
-		|| quote_ident(toponame) || ''.face f2 ''
-		|| ''WHERE f1.face_id > 0 AND f1.face_id < f2.face_id AND ''
-		|| '' Overlaps(topology.ST_GetFaceGeometry(''
-		|| quote_literal(toponame) || '', f1.face_id), ''
-		|| '' topology.ST_GetFaceGeometry(''
-		|| quote_literal(toponame) || '', f2.face_id))''
-	LOOP
-		retrec.error = ''face overlaps face'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-	-- Check for face within face
-	-- TODO: this check requires some thinking, do we really
-	--       have to construct face geometry to detect within condition
-	--       another face ??
-	FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
-		|| quote_ident(toponame) || ''.face f1, ''
-		|| quote_ident(toponame) || ''.face f2 ''
-		|| ''WHERE f1.face_id != 0 AND f2.face_id != 0 ''
-		|| ''AND f1.face_id != f2.face_id ''
-		|| ''AND Within(topology.ST_GetFaceGeometry(''
-		|| quote_literal(toponame) || '', f1.face_id), ''
-		|| '' topology.ST_GetFaceGeometry(''
-		|| quote_literal(toponame) || '', f2.face_id))''
-	LOOP
-		retrec.error = ''face within face'';
-		retrec.id1 = rec.id1;
-		retrec.id2 = rec.id2;
-		RETURN NEXT retrec;
-	END LOOP;
-
-#if 0
-	-- Check SRID consistency
-	FOR rec in EXECUTE
-		''SELECT count(*) FROM ( getSRID(geom) FROM ''
-		|| quote_ident(toponame) || ''.edge ''
-		|| '' UNION ''
-		''SELECT getSRID(geom) FROM ''
-		|| quote_ident(toponame) || ''.node )''
-	LOOP
-		IF rec.count > 1 THEN
-			retrec.error = ''mixed SRIDs'';
-			retrec.id1 = NULL;
-			retrec.id2 = NULL;
-			RETURN NEXT retrec;
-		END IF;
-	END LOOP;
-#endif
-
-	RETURN;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
--- } ValidateTopology(toponame)
-
---{
---  TopoGeo_AddPoint(toponame, pointgeom, layer_id, topogeom_id)
---
---  Add a Point (node) into a topology 
---
-CREATEFUNCTION topology.TopoGeo_AddPoint(varchar, geometry, integer, integer)
-	RETURNS int AS
-'
-DECLARE
-	atopology alias for $1;
-	apoint alias for $2;
-	ret int;
-BEGIN
-
-	-- Add nodes (contained in NO face)
-	SELECT topology.ST_AddIsoNode(atopology, NULL, apoint) INTO ret;
-
-	RAISE NOTICE ''TopoGeo_AddPoint: node: %'', ret;
-
-	RETURN ret;
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} TopoGeo_AddPoint
-
---{
---  TopoGeo_addLinestring
---
---  Add a Point (node) into a topology 
---
-CREATEFUNCTION topology.TopoGeo_addLinestring(varchar, geometry)
-	RETURNS int AS
-'
-DECLARE
-	atopology alias for $1;
-	aline alias for $2;
-	rec RECORD;
-	query text;
-	firstpoint geometry;
-	lastpoint geometry;
-	firstnode int;
-	lastnode int;
-	edgeid int;
-BEGIN
-	firstpoint = StartPoint(aline);
-	lastpoint = EndPoint(aline);
-
-	-- Add first and last point nodes (contained in NO face)
-	SELECT topology.ST_AddIsoNode(atopology, NULL, firstpoint) INTO firstnode;
-	SELECT topology.ST_AddIsoNode(atopology, NULL, lastpoint) INTO lastnode;
-
-	RAISE NOTICE ''First node: %, Last node: %'', firstnode, lastnode;
-
-	-- Add edge 
-	SELECT topology.ST_AddIsoEdge(atopology, firstnode, lastnode, aline)
-		INTO edgeid;
-
-	RAISE NOTICE ''Edge: %'', edgeid;
-
-	RETURN edgeid;
-END
-'
-LANGUAGE 'plpgsql';
---} TopoGeo_addLinestring
-
---{
---  TopoGeo_AddPolygon
---
---  Add a Polygon into a topology 
---
-CREATEFUNCTION topology.TopoGeo_AddPolygon(varchar, geometry)
-	RETURNS int AS
-'
-DECLARE
-	rec RECORD;
-	query text;
-BEGIN
-
-	RAISE EXCEPTION ''TopoGeo_AddPolygon not implemented yet'';
-END
-'
-LANGUAGE 'plpgsql';
---} TopoGeo_AddPolygon
-
---{
---  CreateTopology(name, SRID, precision)
---
--- Create a topology schema, add a topology info record
--- in the topology.topology relation, return it's numeric
--- id. 
---
-CREATEFUNCTION topology.CreateTopology(varchar, integer, float8)
-RETURNS integer
-AS '
-DECLARE
-	atopology alias for $1;
-	srid alias for $2;
-	precision alias for $3;
-	rec RECORD;
-	topology_id integer;
-BEGIN
-
---	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
---	LOOP
---		RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
---	END LOOP;
-
-	------ Fetch next id for the new topology
-	FOR rec IN SELECT nextval(''topology.topology_id_seq'')
-	LOOP
-		topology_id = rec.nextval;
-	END LOOP;
-
-
-	EXECUTE ''
-CREATE SCHEMA '' || quote_ident(atopology) || '';
-	'';
-
-	-------------{ face CREATION
-	EXECUTE 
-	''CREATE TABLE '' || quote_ident(atopology) || ''.face (''
-	|| ''face_id SERIAL,''
-	|| ''mbr BOX2D,''
-	|| '' CONSTRAINT face_primary_key PRIMARY KEY(face_id)''
-	|| '');'';
-	-------------} END OF face CREATION
-
-
-	--------------{ node CREATION
-
-	EXECUTE 
-	''CREATE TABLE '' || quote_ident(atopology) || ''.node (''
-	|| ''node_id SERIAL,''
-	--|| ''geom GEOMETRY,''
-	|| ''containing_face INTEGER,''
-
-	|| ''CONSTRAINT node_primary_key PRIMARY KEY(node_id),''
-
-	--|| ''CONSTRAINT node_geometry_type CHECK ''
-	--|| ''( GeometryType(geom) = ''''POINT'''' ),''
-
-	|| ''CONSTRAINT face_exists FOREIGN KEY(containing_face) ''
-	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id)''
-
-	|| '');'';
-
-	-- Add geometry column to the node table 
-	EXECUTE
-	''SELECT AddGeometryColumn(''||quote_literal(atopology)
-	||'',''''node'''',''''geom'''',''||quote_literal(srid)
-	||'',''''POINT'''',''''2'''')'';
-
-	--------------} END OF node CREATION
-
-	--------------{ edge CREATION
-
-	-- edge_data table
-	EXECUTE 
-	''CREATE TABLE '' || quote_ident(atopology) || ''.edge_data (''
-	|| ''edge_id SERIAL NOT NULL PRIMARY KEY,''
-	|| ''start_node INTEGER NOT NULL,''
-	|| ''end_node INTEGER NOT NULL,''
-	|| ''next_left_edge INTEGER NOT NULL,''
-	|| ''abs_next_left_edge INTEGER NOT NULL,''
-	|| ''next_right_edge INTEGER NOT NULL,''
-	|| ''abs_next_right_edge INTEGER NOT NULL,''
-	|| ''left_face INTEGER NOT NULL,''
-	|| ''right_face INTEGER NOT NULL,''
-	--|| ''geom GEOMETRY NOT NULL,''
-
-	--|| ''CONSTRAINT edge_geometry_type CHECK ''
-	--|| ''( GeometryType(geom) = ''''LINESTRING'''' ),''
-
-	|| ''CONSTRAINT start_node_exists FOREIGN KEY(start_node)''
-	|| '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
-
-	|| ''CONSTRAINT end_node_exists FOREIGN KEY(end_node) ''
-	|| '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
-
-	|| ''CONSTRAINT left_face_exists FOREIGN KEY(left_face) ''
-	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
-
-	|| ''CONSTRAINT right_face_exists FOREIGN KEY(right_face) ''
-	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
-
-	|| ''CONSTRAINT next_left_edge_exists FOREIGN KEY(abs_next_left_edge)''
-	|| '' REFERENCES '' || quote_ident(atopology)
-	|| ''.edge_data(edge_id)''
-	|| '' DEFERRABLE INITIALLY DEFERRED,''
-
-	|| ''CONSTRAINT next_right_edge_exists ''
-	|| ''FOREIGN KEY(abs_next_right_edge)''
-	|| '' REFERENCES '' || quote_ident(atopology)
-	|| ''.edge_data(edge_id) ''
-	|| '' DEFERRABLE INITIALLY DEFERRED''
-	|| '');'';
-
-	-- Add geometry column to the edge_data table 
-	EXECUTE
-	''SELECT AddGeometryColumn(''||quote_literal(atopology)
-	||'',''''edge_data'''',''''geom'''',''||quote_literal(srid)
-	||'',''''LINESTRING'''',''''2'''')'';
-
-
-	-- edge standard view (select rule)
-	EXECUTE ''CREATE VIEW '' || quote_ident(atopology)
-		|| ''.edge AS SELECT ''
-		|| '' edge_id,start_node, end_node, next_left_edge, ''
-		|| '' next_right_edge, ''
-		|| '' left_face, right_face, geom FROM ''
-		|| quote_ident(atopology) || ''.edge_data'';
-
-	-- edge standard view (insert rule)
-	EXECUTE ''CREATE RULE edge_insert_rule AS ON INSERT ''
-        	|| ''TO '' || quote_ident(atopology)
-		|| ''.edge DO INSTEAD ''
-                || '' INSERT into '' || quote_ident(atopology)
-		|| ''.edge_data ''
-                || '' VALUES (NEW.edge_id, NEW.start_node, NEW.end_node, ''
-		|| '' NEW.next_left_edge, abs(NEW.next_left_edge), ''
-		|| '' NEW.next_right_edge, abs(NEW.next_right_edge), ''
-		|| '' NEW.left_face, NEW.right_face, NEW.geom);'';
-
-	--------------} END OF edge CREATION
-
-	--------------{ layer sequence 
-	EXECUTE ''CREATE SEQUENCE ''
-		|| quote_ident(atopology) || ''.layer_id_seq;'';
-	--------------} layer sequence
-
-	--------------{ relation CREATION
-	--
-	EXECUTE 
-	''CREATE TABLE '' || quote_ident(atopology) || ''.relation (''
-	|| '' topogeo_id integer NOT NULL, ''
-	|| '' layer_id integer NOT NULL, '' 
-	|| '' element_id integer NOT NULL, ''
-	|| '' element_type integer NOT NULL, ''
-	|| '' UNIQUE(layer_id,topogeo_id,element_id,element_type));'';
-
-	EXECUTE 
-	''CREATE TRIGGER relation_integrity_checks ''
-	||''BEFORE UPDATE OR INSERT ON ''
-	|| quote_ident(atopology) || ''.relation FOR EACH ROW ''
-	|| '' EXECUTE PROCEDURE topology.RelationTrigger(''
-	||topology_id||'',''||quote_literal(atopology)||'')'';
-	--------------} END OF relation CREATION
-
-	
-	------- Default (world) face
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology) || ''.face(face_id) VALUES(0);'';
-
-	------- GiST index on node
-	EXECUTE ''CREATE INDEX node_gist ON ''
-		|| quote_ident(atopology)
-		|| ''.node using gist (geom gist_geometry_ops);'';
-
-	------- GiST index on edge
-	EXECUTE ''CREATE INDEX edge_gist ON ''
-		|| quote_ident(atopology)
-		|| ''.edge_data using gist (geom gist_geometry_ops);'';
-
-	------- Add record to the "topology" metadata table
-	EXECUTE ''INSERT INTO topology.topology (id, name, srid, precision) ''
-		|| '' VALUES ('' || quote_literal(topology_id) || '',''
-		|| quote_literal(atopology) || '',''
-		|| quote_literal(srid) || '','' || quote_literal(precision)
-		|| '')'';
-
-	RETURN topology_id;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
-
--- wrappers for unspecified srid or precision
-
-CREATEFUNCTION topology.CreateTopology(varchar, integer)
-RETURNS integer AS
-' SELECT topology.CreateTopology($1, $2, -1); '
-LANGUAGE 'SQL' _VOLATILE_STRICT;
-
-CREATEFUNCTION topology.CreateTopology(varchar)
-RETURNS integer AS
-' SELECT topology.CreateTopology($1, -1, -1); '
-LANGUAGE 'SQL' _VOLATILE_STRICT;
-
---} CreateTopology
-
---{
---  DropTopology(name)
---
--- Drops a topology schema getting rid of every dependent object.
---
-CREATEFUNCTION topology.DropTopology(varchar)
-RETURNS text
-AS '
-DECLARE
-	atopology alias for $1;
-	topoid integer;
-	rec RECORD;
-BEGIN
-
-	-- Get topology id
-        SELECT id FROM topology.topology into topoid
-                WHERE name = atopology;
-
-
-	IF topoid IS NOT NULL THEN
-
-		RAISE NOTICE ''Dropping all layers from topology % (%)'',
-			atopology, topoid;
-
-		-- Drop all layers in the topology
-		FOR rec IN EXECUTE ''SELECT * FROM topology.layer WHERE ''
-			|| '' topology_id = '' || topoid
-		LOOP
-
-			EXECUTE ''SELECT topology.DropTopoGeometryColumn(''
-				|| quote_literal(rec.schema_name)
-				|| '',''
-				|| quote_literal(rec.table_name)
-				|| '',''
-				|| quote_literal(rec.feature_column)
-				|| '')'';
-		END LOOP;
-
-		-- Delete record from topology.topology
-		EXECUTE ''DELETE FROM topology.topology WHERE id = ''
-			|| topoid;
-
-	END IF;
-
-
-	-- Drop the schema (if it exists)
-	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
-	LOOP
-		EXECUTE ''DROP SCHEMA ''||quote_ident(atopology)||'' CASCADE'';
-	END LOOP;
-
-
-	RETURN ''Topology '' || quote_literal(atopology) || '' dropped'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} DropTopology
-
---={ ----------------------------------------------------------------
---  POSTGIS-SPECIFIC topology predicates
---
---  This part contains function NOT in the SQL/MM specification
---
----------------------------------------------------------------------
-
---{
--- Intersects(TopoGeometry, TopoGeometry)
---
-CREATEFUNCTION topology.intersects(topology.TopoGeometry, topology.TopoGeometry)
-	RETURNS bool
-AS
-'
-DECLARE
-	tg1 alias for $1;
-	tg2 alias for $2;
-	tgbuf topology.TopoGeometry;
-	rec RECORD;
-	toponame varchar;
-	query text;
-BEGIN
-	IF tg1.topology_id != tg2.topology_id THEN
-		RAISE EXCEPTION ''Cannot compute intersection between TopoGeometries from different topologies'';
-	END IF;
-
-	-- Order TopoGeometries so that tg1 has less-or-same
-	-- dimensionality of tg1 (point,line,polygon,collection)
-	IF tg1.type > tg2.type THEN
-		tgbuf := tg2;
-		tg2 := tg1;
-		tg1 := tgbuf;
-	END IF;
-
-	--RAISE NOTICE ''tg1.id:% tg2.id:%'', tg1.id, tg2.id;
-	-- Geometry collection are not currently supported
-	IF tg2.type = 4 THEN
-		RAISE EXCEPTION ''GeometryCollection are not supported by intersects()'';
-	END IF;
-
-        -- Get topology name
-        SELECT name FROM topology.topology into toponame
-                WHERE id = tg1.topology_id;
-
-	-- Hierarchical TopoGeometries are not currently supported
-	query = ''SELECT level FROM topology.layer''
-		|| '' WHERE ''
-		|| '' topology_id = '' || tg1.topology_id
-		|| '' AND ''
-		|| ''( layer_id = '' || tg1.layer_id
-		|| '' OR layer_id = '' || tg2.layer_id
-		|| '' ) ''
-		|| '' AND level > 0 '';
-
-	--RAISE NOTICE ''%'', query;
-
-	FOR rec IN EXECUTE query
-	LOOP
-		RAISE EXCEPTION ''Hierarchical TopoGeometries are not currently supported by intersects()'';
-	END LOOP;
-
-	IF tg1.type = 1 THEN -- [multi]point
-
-
-		IF tg2.type = 1 THEN -- point/point
-	---------------------------------------------------------
-	-- 
-	--  Two [multi]point features intersect if they share
-	--  any Node 
-	--
-	--
-	--
-			query =
-				''SELECT a.topogeo_id FROM ''
-				|| quote_ident(toponame) ||
-				''.relation a, ''
-				|| quote_ident(toponame) ||
-				''.relation b ''
-				|| ''WHERE a.layer_id = '' || tg1.layer_id
-				|| '' AND b.layer_id = '' || tg2.layer_id
-				|| '' AND a.topogeo_id = '' || tg1.id
-				|| '' AND b.topogeo_id = '' || tg2.id
-				|| '' AND a.element_id = b.element_id ''
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				RETURN TRUE; -- they share an element
-			END LOOP;
-			RETURN FALSE; -- no elements shared
-	--
-	---------------------------------------------------------
-			
-
-		ELSIF tg2.type = 2 THEN -- point/line
-	---------------------------------------------------------
-	-- 
-	--  A [multi]point intersects a [multi]line if they share
-	--  any Node. 
-	--
-	--
-	--
-			query =
-				''SELECT a.topogeo_id FROM ''
-				|| quote_ident(toponame) ||
-				''.relation a, ''
-				|| quote_ident(toponame) ||
-				''.relation b, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e ''
-				|| ''WHERE a.layer_id = '' || tg1.layer_id
-				|| '' AND b.layer_id = '' || tg2.layer_id
-				|| '' AND a.topogeo_id = '' || tg1.id
-				|| '' AND b.topogeo_id = '' || tg2.id
-				|| '' AND abs(b.element_id) = e.edge_id ''
-				|| '' AND ( ''
-					|| '' e.start_node = a.element_id ''
-					|| '' OR ''
-					|| '' e.end_node = a.element_id ''
-				|| '' )''
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				RETURN TRUE; -- they share an element
-			END LOOP;
-			RETURN FALSE; -- no elements shared
-	--
-	---------------------------------------------------------
-
-		ELSIF tg2.type = 3 THEN -- point/polygon
-	---------------------------------------------------------
-	-- 
-	--  A [multi]point intersects a [multi]polygon if any
-	--  Node of the point is contained in any face of the
-	--  polygon OR ( is end_node or start_node of any edge
-	--  of any polygon face ).
-	--
-	--  We assume the Node-in-Face check is faster becasue
-	--  there will be less Faces then Edges in any polygon.
-	--
-	--
-	--
-	--
-			-- Check if any node is contained in a face
-			query =
-				''SELECT n.node_id as id FROM ''
-				|| quote_ident(toponame) ||
-				''.relation r1, ''
-				|| quote_ident(toponame) ||
-				''.relation r2, ''
-				|| quote_ident(toponame) ||
-				''.node n ''
-				|| ''WHERE r1.layer_id = '' || tg1.layer_id
-				|| '' AND r2.layer_id = '' || tg2.layer_id
-				|| '' AND r1.topogeo_id = '' || tg1.id
-				|| '' AND r2.topogeo_id = '' || tg2.id
-				|| '' AND n.node_id = r1.element_id ''
-				|| '' AND r2.element_id = n.containing_face ''
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				--RAISE NOTICE ''Node % in polygon face'', rec.id;
-				RETURN TRUE; -- one (or more) nodes are
-				             -- contained in a polygon face
-			END LOOP;
-
-			-- Check if any node is start or end of any polygon
-			-- face edge
-			query =
-				''SELECT n.node_id as nid, e.edge_id as eid ''
-				|| '' FROM ''
-				|| quote_ident(toponame) ||
-				''.relation r1, ''
-				|| quote_ident(toponame) ||
-				''.relation r2, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e, ''
-				|| quote_ident(toponame) ||
-				''.node n ''
-				|| ''WHERE r1.layer_id = '' || tg1.layer_id
-				|| '' AND r2.layer_id = '' || tg2.layer_id
-				|| '' AND r1.topogeo_id = '' || tg1.id
-				|| '' AND r2.topogeo_id = '' || tg2.id
-				|| '' AND n.node_id = r1.element_id ''
-				|| '' AND ( ''
-				|| '' e.left_face = r2.element_id ''
-				|| '' OR ''
-				|| '' e.right_face = r2.element_id ''
-				|| '' ) ''
-				|| '' AND ( ''
-				|| '' e.start_node = r1.element_id ''
-				|| '' OR ''
-				|| '' e.end_node = r1.element_id ''
-				|| '' ) ''
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				--RAISE NOTICE ''Node % on edge % bound'', rec.nid, rec.eid;
-				RETURN TRUE; -- one node is start or end
-				             -- of a face edge
-			END LOOP;
-
-			RETURN FALSE; -- no intersection
-	--
-	---------------------------------------------------------
-
-		ELSIF tg2.type = 4 THEN -- point/collection
-			RAISE EXCEPTION ''Intersection point/collection not implemented yet'';
-
-		ELSE
-			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
-		END IF;
-
-	ELSIF tg1.type = 2 THEN -- [multi]line
-		IF tg2.type = 2 THEN -- line/line
-	---------------------------------------------------------
-	-- 
-	--  A [multi]line intersects a [multi]line if they share
-	--  any Node. 
-	--
-	--
-	--
-			query =
-				''SELECT e1.start_node FROM ''
-				|| quote_ident(toponame) ||
-				''.relation r1, ''
-				|| quote_ident(toponame) ||
-				''.relation r2, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e1, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e2 ''
-				|| ''WHERE r1.layer_id = '' || tg1.layer_id
-				|| '' AND r2.layer_id = '' || tg2.layer_id
-				|| '' AND r1.topogeo_id = '' || tg1.id
-				|| '' AND r2.topogeo_id = '' || tg2.id
-				|| '' AND abs(r1.element_id) = e1.edge_id ''
-				|| '' AND abs(r2.element_id) = e2.edge_id ''
-				|| '' AND ( ''
-					|| '' e1.start_node = e2.start_node ''
-					|| '' OR ''
-					|| '' e1.start_node = e2.end_node ''
-					|| '' OR ''
-					|| '' e1.end_node = e2.start_node ''
-					|| '' OR ''
-					|| '' e1.end_node = e2.end_node ''
-				|| '' )''
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				RETURN TRUE; -- they share an element
-			END LOOP;
-			RETURN FALSE; -- no elements shared
-	--
-	---------------------------------------------------------
-
-		ELSIF tg2.type = 3 THEN -- line/polygon
-	---------------------------------------------------------
-	-- 
-	-- A [multi]line intersects a [multi]polygon if they share
-	-- any Node (touch-only case), or if any line edge has any
-	-- polygon face on the left or right (full-containment case
-	-- + edge crossing case).
-	--
-	--
-			-- E1 are line edges, E2 are polygon edges
-			-- R1 are line relations.
-			-- R2 are polygon relations.
-			-- R2.element_id are FACE ids
-			query =
-				''SELECT e1.edge_id''
-				|| '' FROM ''
-				|| quote_ident(toponame) ||
-				''.relation r1, ''
-				|| quote_ident(toponame) ||
-				''.relation r2, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e1, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e2 ''
-				|| ''WHERE r1.layer_id = '' || tg1.layer_id
-				|| '' AND r2.layer_id = '' || tg2.layer_id
-				|| '' AND r1.topogeo_id = '' || tg1.id
-				|| '' AND r2.topogeo_id = '' || tg2.id
-
-				-- E1 are line edges
-				|| '' AND e1.edge_id = abs(r1.element_id) ''
-
-				-- E2 are face edges
-				|| '' AND ( e2.left_face = r2.element_id ''
-				|| ''   OR e2.right_face = r2.element_id ) ''
-
-				|| '' AND ( ''
-
-				-- Check if E1 have left-or-right face 
-				-- being part of R2.element_id
-				|| '' e1.left_face = r2.element_id ''
-				|| '' OR ''
-				|| '' e1.right_face = r2.element_id ''
-
-				-- Check if E1 share start-or-end node
-				-- with any E2.
-				|| '' OR ''
-				|| '' e1.start_node = e2.start_node ''
-				|| '' OR ''
-				|| '' e1.start_node = e2.end_node ''
-				|| '' OR ''
-				|| '' e1.end_node = e2.start_node ''
-				|| '' OR ''
-				|| '' e1.end_node = e2.end_node ''
-
-				|| '' ) ''
-
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				RETURN TRUE; -- either common node
-				             -- or edge-in-face
-			END LOOP;
-
-			RETURN FALSE; -- no intersection
-	--
-	---------------------------------------------------------
-
-		ELSIF tg2.type = 4 THEN -- line/collection
-			RAISE EXCEPTION ''Intersection line/collection not implemented yet'', tg1.type, tg2.type;
-
-		ELSE
-			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
-		END IF;
-
-
-	ELSIF tg1.type = 3 THEN -- [multi]polygon
-
-		IF tg2.type = 3 THEN -- polygon/polygon
-	---------------------------------------------------------
-	-- 
-	-- A [multi]polygon intersects a [multi]polygon if they share
-	-- any Node (touch-only case), or if any face edge has any of the
-	-- other polygon face on the left or right (full-containment case
-	-- + edge crossing case).
-	--
-	--
-			-- E1 are poly1 edges.
-			-- E2 are poly2 edges
-			-- R1 are poly1 relations.
-			-- R2 are poly2 relations.
-			-- R1.element_id are poly1 FACE ids
-			-- R2.element_id are poly2 FACE ids
-			query =
-				''SELECT e1.edge_id''
-				|| '' FROM ''
-				|| quote_ident(toponame) ||
-				''.relation r1, ''
-				|| quote_ident(toponame) ||
-				''.relation r2, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e1, ''
-				|| quote_ident(toponame) ||
-				''.edge_data e2 ''
-				|| ''WHERE r1.layer_id = '' || tg1.layer_id
-				|| '' AND r2.layer_id = '' || tg2.layer_id
-				|| '' AND r1.topogeo_id = '' || tg1.id
-				|| '' AND r2.topogeo_id = '' || tg2.id
-
-				-- E1 are poly1 edges
-				|| '' AND ( e1.left_face = r1.element_id ''
-				|| ''   OR e1.right_face = r1.element_id ) ''
-
-				-- E2 are poly2 edges
-				|| '' AND ( e2.left_face = r2.element_id ''
-				|| ''   OR e2.right_face = r2.element_id ) ''
-
-				|| '' AND ( ''
-
-				-- Check if any edge from a polygon face
-				-- has any of the other polygon face
-				-- on the left or right 
-				|| '' e1.left_face = r2.element_id ''
-				|| '' OR ''
-				|| '' e1.right_face = r2.element_id ''
-				|| '' OR ''
-				|| '' e2.left_face = r1.element_id ''
-				|| '' OR ''
-				|| '' e2.right_face = r1.element_id ''
-
-				-- Check if E1 share start-or-end node
-				-- with any E2.
-				|| '' OR ''
-				|| '' e1.start_node = e2.start_node ''
-				|| '' OR ''
-				|| '' e1.start_node = e2.end_node ''
-				|| '' OR ''
-				|| '' e1.end_node = e2.start_node ''
-				|| '' OR ''
-				|| '' e1.end_node = e2.end_node ''
-
-				|| '' ) ''
-
-				|| '' LIMIT 1'';
-			--RAISE NOTICE ''%'', query;
-			FOR rec IN EXECUTE query
-			LOOP
-				RETURN TRUE; -- either common node
-				             -- or edge-in-face
-			END LOOP;
-
-			RETURN FALSE; -- no intersection
-	--
-	---------------------------------------------------------
-
-		ELSIF tg2.type = 4 THEN -- polygon/collection
-			RAISE EXCEPTION ''Intersection poly/collection not implemented yet'', tg1.type, tg2.type;
-
-		ELSE
-			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
-		END IF;
-
-	ELSIF tg1.type = 4 THEN -- collection
-		IF tg2.type = 4 THEN -- collection/collection
-			RAISE EXCEPTION ''Intersection collection/collection not implemented yet'', tg1.type, tg2.type;
-		ELSE
-			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
-		END IF;
-
-	ELSE
-		RAISE EXCEPTION ''Invalid TopoGeometry type %'', tg1.type;
-	END IF;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} intersects(TopoGeometry, TopoGeometry)
-
---{
---  equals(TopoGeometry, TopoGeometry)
---
-CREATEFUNCTION topology.equals(topology.TopoGeometry, topology.TopoGeometry)
-	RETURNS bool
-AS
-'
-DECLARE
-	tg1 alias for $1;
-	tg2 alias for $2;
-	rec RECORD;
-	toponame varchar;
-	query text;
-BEGIN
-
-	IF tg1.topology_id != tg2.topology_id THEN
-		RAISE EXCEPTION ''Cannot compare TopoGeometries from different topologies'';
-	END IF;
-
-	-- Not the same type, not equal
-	IF tg1.type != tg2.type THEN
-		RETURN FALSE;
-	END IF;
-
-	-- Geometry collection are not currently supported
-	IF tg2.type = 4 THEN
-		RAISE EXCEPTION ''GeometryCollection are not supported by equals()'';
-	END IF;
-
-        -- Get topology name
-        SELECT name FROM topology.topology into toponame
-                WHERE id = tg1.topology_id;
-
-	-- Two geometries are equal if they are composed by 
-	-- the same TopoElements
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| '' topology.GetTopoGeomElements(''
-		|| quote_literal(toponame) || '', ''
-		|| tg1.layer_id || '','' || tg1.id || '') ''
-		|| '' EXCEPT SELECT * FROM ''
-		|| '' topology.GetTopogeomElements(''
-		|| quote_literal(toponame) || '', ''
-		|| tg2.layer_id || '','' || tg2.id || '');''
-	LOOP
-		RETURN FALSE;
-	END LOOP;
-
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| '' topology.GetTopoGeomElements(''
-		|| quote_literal(toponame) || '', ''
-		|| tg2.layer_id || '','' || tg2.id || '')''
-		|| '' EXCEPT SELECT * FROM ''
-		|| '' topology.GetTopogeomElements(''
-		|| quote_literal(toponame) || '', ''
-		|| tg1.layer_id || '','' || tg1.id || ''); ''
-	LOOP
-		RETURN FALSE;
-	END LOOP;
-	RETURN TRUE;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE_STRICT;
---} equals(TopoGeometry, TopoGeometry)
-
---=} POSTGIS-SPECIFIC topology predicates
-
-
---=}  POSTGIS-SPECIFIC block
-
---={ ----------------------------------------------------------------
---  SQL/MM block
---
---  This part contains function in the SQL/MM specification
---
----------------------------------------------------------------------
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.5
---
---  ST_GetFaceEdges(atopology, aface)
---
---
--- 
-CREATEFUNCTION topology.ST_GetFaceEdges(varchar, integer)
-	RETURNS setof topology.GetFaceEdges_ReturnType
-AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	aface ALIAS FOR $2;
-	rec RECORD;
-BEGIN
-	--
-	-- Atopology and aface are required
-	-- 
-	IF atopology IS NULL OR aface IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	RAISE EXCEPTION
-		 ''ST_GetFaceEdges: not implemented yet'';
-
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_GetFaceEdges
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.16
---
---  ST_GetFaceGeometry(atopology, aface)
--- 
-CREATEFUNCTION topology.ST_GetFaceGeometry(varchar, integer)
-	RETURNS GEOMETRY AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	aface ALIAS FOR $2;
-	rec RECORD;
-BEGIN
-	--
-	-- Atopology and aface are required
-	-- 
-	IF atopology IS NULL OR aface IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Construct face 
-	-- 
-	FOR rec IN EXECUTE ''SELECT polygonize(geom) FROM ( SELECT geom FROM ''
-		|| quote_ident(atopology)
-		|| ''.edge WHERE left_face = '' || aface || 
-		'' OR right_face = '' || aface || '') as foo''
-	LOOP
-		RETURN geometryN(rec.polygonize, 1);
-		--RETURN rec.polygonize;
-	END LOOP;
-
-
-	--
-	-- No face found
-	-- 
-	RAISE EXCEPTION
-	''SQL/MM Spatial exception - non-existent face.'';
-END
-' 
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_GetFaceGeometry
-
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.1 
---
---  ST_AddIsoNode(atopology, aface, apoint)
---
-CREATEFUNCTION topology.ST_AddIsoNode(varchar, integer, geometry)
-	RETURNS INTEGER AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	aface ALIAS FOR $2;
-	apoint ALIAS FOR $3;
-	rec RECORD;
-	nodeid integer;
-BEGIN
-
-	--
-	-- Atopology and apoint are required
-	-- 
-	IF atopology IS NULL OR apoint IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Apoint must be a point
-	--
-	IF substring(geometrytype(apoint), 1, 5) != ''POINT''
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - invalid point'';
-	END IF;
-
-	--
-	-- Check if a coincident node already exists
-	-- 
-	-- We use index AND x/y equality
-	--
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node '' ||
-		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
-		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
-		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - coincident node'';
-	END LOOP;
-
-	--
-	-- Check if any edge crosses (intersects) this node
-	-- I used _intersects_ here to include boundaries (endpoints)
-	--
-	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-		|| quote_ident(atopology) || ''.edge '' 
-		|| ''WHERE geom && '' || quote_literal(apoint) 
-		|| '' AND intersects(geom, '' || quote_literal(apoint)
-		|| '')''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception ­ edge crosses node.'';
-	END LOOP;
-
-
-	--
-	-- Verify that aface contains apoint
-	-- if aface is null no check is done
-	--
-	IF aface IS NOT NULL THEN
-
-	FOR rec IN EXECUTE ''SELECT within(''
-		|| quote_literal(apoint) || ''::geometry, 
-		topology.ST_GetFaceGeometry(''
-		|| quote_literal(atopology) || '', '' || aface ||
-		''))''
-	LOOP
-		IF rec.within = ''f'' THEN
-			RAISE EXCEPTION
-			''SQL/MM Spatial exception - not within face'';
-		ELSIF rec.within IS NULL THEN
-			RAISE EXCEPTION
-			''SQL/MM Spatial exception - non-existent face'';
-		END IF;
-	END LOOP;
-
-	END IF;
-
-	--
-	-- Get new node id from sequence
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.node_node_id_seq'''')''
-	LOOP
-		nodeid = rec.nextval;
-	END LOOP;
-
-	--
-	-- Insert the new row
-	--
-	IF aface IS NOT NULL THEN
-		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-			|| ''.node(node_id, geom, containing_face) 
-			VALUES(''||nodeid||'',''||quote_literal(apoint)||
-			'',''||aface||'')'';
-	ELSE
-		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-			|| ''.node(node_id, geom) 
-			VALUES(''||nodeid||'',''||quote_literal(apoint)||
-			'')'';
-	END IF;
-
-	RETURN nodeid;
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.2 
---
---  ST_MoveIsoNode(atopology, anode, apoint)
---
-CREATEFUNCTION topology.ST_MoveIsoNode(varchar, integer, geometry)
-	RETURNS TEXT AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anode ALIAS FOR $2;
-	apoint ALIAS FOR $3;
-	rec RECORD;
-BEGIN
-
-	--
-	-- All arguments are required
-	-- 
-	IF atopology IS NULL OR anode IS NULL OR apoint IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Apoint must be a point
-	--
-	IF substring(geometrytype(apoint), 1, 5) != ''POINT''
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - invalid point'';
-	END IF;
-
-	--
-	-- Check node isolation.
-	-- 
-	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-		|| quote_ident(atopology) || ''.edge '' ||
-		'' WHERE start_node =  '' || anode ||
-		'' OR end_node = '' || anode 
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - not isolated node'';
-	END LOOP;
-
-	--
-	-- Check if a coincident node already exists
-	-- 
-	-- We use index AND x/y equality
-	--
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node '' ||
-		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
-		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
-		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - coincident node'';
-	END LOOP;
-
-	--
-	-- Check if any edge crosses (intersects) this node
-	-- I used _intersects_ here to include boundaries (endpoints)
-	--
-	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-		|| quote_ident(atopology) || ''.edge '' 
-		|| ''WHERE geom && '' || quote_literal(apoint) 
-		|| '' AND intersects(geom, '' || quote_literal(apoint)
-		|| '')''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception ­ edge crosses node.'';
-	END LOOP;
-
-	--
-	-- Update node point
-	--
-	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.node ''
-		|| '' SET geom = '' || quote_literal(apoint) 
-		|| '' WHERE node_id = '' || anode;
-
-	RETURN ''Isolated Node '' || anode || '' moved to location ''
-		|| x(apoint) || '','' || y(apoint);
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_MoveIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.3 
---
---  ST_RemoveIsoNode(atopology, anode)
---
-CREATEFUNCTION topology.ST_RemoveIsoNode(varchar, integer)
-	RETURNS TEXT AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anode ALIAS FOR $2;
-	rec RECORD;
-BEGIN
-
-	--
-	-- Atopology and apoint are required
-	-- 
-	IF atopology IS NULL OR anode IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Check node isolation.
-	-- 
-	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' ||
-		'' WHERE start_node =  '' || anode ||
-		'' OR end_node = '' || anode 
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - not isolated node'';
-	END LOOP;
-
-	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.node ''
-		|| '' WHERE node_id = '' || anode;
-
-	RETURN ''Isolated node '' || anode || '' removed'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_RemoveIsoNode
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.7 
---
---  ST_RemoveIsoEdge(atopology, anedge)
---
-CREATEFUNCTION topology.ST_RemoveIsoEdge(varchar, integer)
-	RETURNS TEXT AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anedge ALIAS FOR $2;
-	edge RECORD;
-	rec RECORD;
-	ok BOOL;
-BEGIN
-
-	--
-	-- Atopology and anedge are required
-	-- 
-	IF atopology IS NULL OR anedge IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Check node existance
-	-- 
-	ok = false;
-	FOR edge IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' ||
-		'' WHERE edge_id =  '' || anedge
-	LOOP
-		ok = true;
-	END LOOP;
-	IF NOT ok THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - non-existent edge'';
-	END IF;
-
-	--
-	-- Check node isolation
-	-- 
-	IF edge.left_face != edge.right_face THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - not isolated edge'';
-	END IF;
-
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' 
-		|| '' WHERE edge_id !=  '' || anedge
-		|| '' AND ( start_node = '' || edge.start_node
-		|| '' OR start_node = '' || edge.end_node
-		|| '' OR end_node = '' || edge.start_node
-		|| '' OR end_node = '' || edge.end_node
-		|| '' ) ''
-	LOOP
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - not isolated edge'';
-	END LOOP;
-
-	--
-	-- Delete the edge
-	--
-	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
-		|| '' WHERE edge_id = '' || anedge;
-
-	RETURN ''Isolated edge '' || anedge || '' removed'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_RemoveIsoEdge
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.8 
---
---  ST_NewEdgesSplit(atopology, anedge, apoint)
---
-CREATEFUNCTION topology.ST_NewEdgesSplit(varchar, integer, geometry)
-	RETURNS INTEGER AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anedge ALIAS FOR $2;
-	apoint ALIAS FOR $3;
-	oldedge RECORD;
-	rec RECORD;
-	tmp integer;
-	topoid integer;
-	nodeid integer;
-	nodepos float8;
-	edgeid1 integer;
-	edgeid2 integer;
-	edge1 geometry;
-	edge2 geometry;
-	ok BOOL;
-BEGIN
-
-	--
-	-- All args required
-	-- 
-	IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Check node existance
-	-- 
-	ok = false;
-	FOR oldedge IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' ||
-		'' WHERE edge_id =  '' || anedge
-	LOOP
-		ok = true;
-	END LOOP;
-	IF NOT ok THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - non-existent edge'';
-	END IF;
-
-	--
-	-- Check that given point is Within(anedge.geom)
-	-- 
-	IF NOT within(apoint, oldedge.geom) THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - point not on edge'';
-	END IF;
-
-	--
-	-- Check if a coincident node already exists
-	--
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node '' ||
-		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
-		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
-		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - coincident node'';
-	END LOOP;
-
-	--
-	-- Get new node id
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.node_node_id_seq'''')''
-	LOOP
-		nodeid = rec.nextval;
-	END LOOP;
-
-	--RAISE NOTICE ''Next node id = % '', nodeid;
-
-	--
-	-- Add the new node 
-	--
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.node(node_id, geom) 
-		VALUES(''||nodeid||'',''||quote_literal(apoint)||
-		'')'';
-
-	--
-	-- Delete the old edge
-	--
-	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
-		|| '' WHERE edge_id = '' || anedge;
-
-	--
-	-- Compute new edges
-	--
-	nodepos = line_locate_point(oldedge.geom, apoint);
-	edge1 = line_substring(oldedge.geom, 0, nodepos);
-	edge2 = line_substring(oldedge.geom, nodepos, 1);
-
-	--
-	-- Get ids for the new edges 
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.edge_data_edge_id_seq'''')''
-	LOOP
-		edgeid1 = rec.nextval;
-	END LOOP;
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.edge_data_edge_id_seq'''')''
-	LOOP
-		edgeid2 = rec.nextval;
-	END LOOP;
-
-	--RAISE NOTICE ''EdgeId1 % EdgeId2 %'', edgeid1, edgeid2;
-
-	--
-	-- Insert the two new edges
-	--
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.edge VALUES(''
-		||edgeid1||'',''||oldedge.start_node
-		||'',''||nodeid
-		||'',''||edgeid2
-		||'',''||oldedge.next_right_edge
-		||'',''||oldedge.left_face
-		||'',''||oldedge.right_face
-		||'',''||quote_literal(edge1)
-		||'')'';
-
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.edge VALUES(''
-		||edgeid2||'',''||nodeid
-		||'',''||oldedge.end_node
-		||'',''||oldedge.next_left_edge
-		||'',-''||edgeid1
-		||'',''||oldedge.left_face
-		||'',''||oldedge.right_face
-		||'',''||quote_literal(edge2)
-		||'')'';
-
-	--
-	-- Update all next edge references to match new layout
-	--
-
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET next_right_edge = ''
-		|| edgeid2
-		|| '',''
-		|| '' abs_next_right_edge = '' || edgeid2
-		|| '' WHERE next_right_edge = '' || anedge;
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET next_right_edge = ''
-		|| -edgeid1
-		|| '',''
-		|| '' abs_next_right_edge = '' || edgeid1
-		|| '' WHERE next_right_edge = '' || -anedge;
-
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET next_left_edge = ''
-		|| edgeid1
-		|| '',''
-		|| '' abs_next_left_edge = '' || edgeid1
-		|| '' WHERE next_left_edge = '' || anedge;
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET ''
-		|| '' next_left_edge = '' || -edgeid2
-		|| '',''
-		|| '' abs_next_left_edge = '' || edgeid2
-		|| '' WHERE next_left_edge = '' || -anedge;
-
-	-- Get topology id
-        SELECT id FROM topology.topology into topoid
-                WHERE name = atopology;
-
-	--
-	-- Update references in the Relation table.
-	-- We only take into considerations non-hierarchical
-	-- TopoGeometry here, for obvious reasons.
-	--
-	FOR rec IN EXECUTE ''SELECT r.* FROM ''
-		|| quote_ident(atopology)
-		|| ''.relation r, topology.layer l ''
-		|| '' WHERE ''
-		|| '' l.topology_id = '' || topoid
-		|| '' AND l.level = 0 ''
-		|| '' AND l.layer_id = r.layer_id ''
-		|| '' AND abs(r.element_id) = '' || anedge
-		|| '' AND r.element_type = 2''
-	LOOP
-		--RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split'', rec.topogeo_id, rec.layer_id;
-
-		-- Delete old reference
-		EXECUTE ''DELETE FROM '' || quote_ident(atopology)
-			|| ''.relation ''
-			|| '' WHERE ''
-			|| ''layer_id = '' || rec.layer_id
-			|| '' AND ''
-			|| ''topogeo_id = '' || rec.topogeo_id
-			|| '' AND ''
-			|| ''element_type = '' || rec.element_type
-			|| '' AND ''
-			|| ''abs(element_id) = '' || anedge;
-
-		-- Add new reference to edge1
-		IF rec.element_id < 0 THEN
-			tmp = -edgeid1;
-		ELSE
-			tmp = edgeid1;
-		END IF;
-		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-			|| ''.relation ''
-			|| '' VALUES( ''
-			|| rec.topogeo_id
-			|| '',''
-			|| rec.layer_id
-			|| '',''
-			|| tmp
-			|| '',''
-			|| rec.element_type
-			|| '')'';
-
-		-- Add new reference to edge2
-		IF rec.element_id < 0 THEN
-			tmp = -edgeid2;
-		ELSE
-			tmp = edgeid2;
-		END IF;
-		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-			|| ''.relation ''
-			|| '' VALUES( ''
-			|| rec.topogeo_id
-			|| '',''
-			|| rec.layer_id
-			|| '',''
-			|| tmp
-			|| '',''
-			|| rec.element_type
-			|| '')'';
-			
-	END LOOP;
-
-	--RAISE NOTICE ''Edge % split in edges % and % by node %'',
-	--	anedge, edgeid1, edgeid2, nodeid;
-
-	RETURN nodeid; 
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_NewEdgesSplit
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.9 
---
---  ST_ModEdgesSplit(atopology, anedge, apoint)
---
-CREATEFUNCTION topology.ST_ModEdgesSplit(varchar, integer, geometry)
-	RETURNS INTEGER AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anedge ALIAS FOR $2;
-	apoint ALIAS FOR $3;
-	oldedge RECORD;
-	rec RECORD;
-	tmp integer;
-	topoid integer;
-	nodeid integer;
-	nodepos float8;
-	newedgeid integer;
-	newedge1 geometry;
-	newedge2 geometry;
-	query text;
-	ok BOOL;
-BEGIN
-
-	--
-	-- All args required
-	-- 
-	IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Check node existance
-	-- 
-	ok = false;
-	FOR oldedge IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' ||
-		'' WHERE edge_id =  '' || anedge
-	LOOP
-		ok = true;
-	END LOOP;
-	IF NOT ok THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - non-existent edge'';
-	END IF;
-
-	--
-	-- Check that given point is Within(anedge.geom)
-	-- 
-	IF NOT within(apoint, oldedge.geom) THEN
-		RAISE EXCEPTION
-		  ''SQL/MM Spatial exception - point not on edge'';
-	END IF;
-
-	--
-	-- Check if a coincident node already exists
-	--
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node '' ||
-		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
-		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
-		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - coincident node'';
-	END LOOP;
-
-	--
-	-- Get new node id
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.node_node_id_seq'''')''
-	LOOP
-		nodeid = rec.nextval;
-	END LOOP;
-
-	--RAISE NOTICE ''Next node id = % '', nodeid;
-
-	--
-	-- Add the new node 
-	--
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.node(node_id, geom) 
-		VALUES(''||nodeid||'',''||quote_literal(apoint)||
-		'')'';
-
-	--
-	-- Compute new edge
-	--
-	nodepos = line_locate_point(oldedge.geom, apoint);
-	newedge1 = line_substring(oldedge.geom, 0, nodepos);
-	newedge2 = line_substring(oldedge.geom, nodepos, 1);
-
-
-	--
-	-- Get ids for the new edge
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.edge_data_edge_id_seq'''')''
-	LOOP
-		newedgeid = rec.nextval;
-	END LOOP;
-
-	--
-	-- Insert the new edge
-	--
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.edge ''
-		|| ''(edge_id, start_node, end_node,''
-		|| ''next_left_edge, next_right_edge,''
-		|| ''left_face, right_face, geom) ''
-		|| ''VALUES(''
-		||newedgeid||'',''||nodeid
-		||'',''||oldedge.end_node
-		||'',''||oldedge.next_left_edge
-		||'',-''||anedge
-		||'',''||oldedge.left_face
-		||'',''||oldedge.right_face
-		||'',''||quote_literal(newedge2)
-		||'')'';
-
-	--
-	-- Update the old edge
-	--
-	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
-		|| '' SET geom = '' || quote_literal(newedge1)
-		|| '',''
-		|| '' next_left_edge = '' || newedgeid
-		|| '',''
-		|| '' end_node = '' || nodeid
-		|| '' WHERE edge_id = '' || anedge;
-
-
-	--
-	-- Update all next edge references to match new layout
-	--
-
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET next_right_edge = ''
-		|| -newedgeid 
-		|| '',''
-		|| '' abs_next_right_edge = '' || newedgeid
-		|| '' WHERE next_right_edge = '' || -anedge;
-
-	EXECUTE ''UPDATE '' || quote_ident(atopology)
-		|| ''.edge_data SET ''
-		|| '' next_left_edge = '' || -newedgeid
-		|| '',''
-		|| '' abs_next_left_edge = '' || newedgeid
-		|| '' WHERE next_left_edge = '' || -anedge;
-
-	-- Get topology id
-        SELECT id FROM topology.topology into topoid
-                WHERE name = atopology;
-
-	--
-	-- Update references in the Relation table.
-	-- We only take into considerations non-hierarchical
-	-- TopoGeometry here, for obvious reasons.
-	--
-	FOR rec IN EXECUTE ''SELECT r.* FROM ''
-		|| quote_ident(atopology)
-		|| ''.relation r, topology.layer l ''
-		|| '' WHERE ''
-		|| '' l.topology_id = '' || topoid
-		|| '' AND l.level = 0 ''
-		|| '' AND l.layer_id = r.layer_id ''
-		|| '' AND abs(r.element_id) = '' || anedge
-		|| '' AND r.element_type = 2''
-	LOOP
-		--RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split (%) - updating to add new edge %'', rec.topogeo_id, rec.layer_id, anedge, newedgeid;
-
-		-- Add new reference to edge1
-		IF rec.element_id < 0 THEN
-			tmp = -newedgeid;
-		ELSE
-			tmp = newedgeid;
-		END IF;
-		query = ''INSERT INTO '' || quote_ident(atopology)
-			|| ''.relation ''
-			|| '' VALUES( ''
-			|| rec.topogeo_id
-			|| '',''
-			|| rec.layer_id
-			|| '',''
-			|| tmp
-			|| '',''
-			|| rec.element_type
-			|| '')'';
-
-		--RAISE NOTICE ''%'', query;
-		EXECUTE query;
-	END LOOP;
-
-	--RAISE NOTICE ''Edge % split in edges % and % by node %'',
-	--	anedge, anedge, newedgeid, nodeid;
-
-	RETURN nodeid; 
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_ModEdgesSplit
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.4 
---
---  ST_AddIsoEdge(atopology, anode, anothernode, acurve)
--- 
-CREATEFUNCTION topology.ST_AddIsoEdge(varchar, integer, integer, geometry)
-	RETURNS INTEGER AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anode ALIAS FOR $2;
-	anothernode ALIAS FOR $3;
-	acurve ALIAS FOR $4;
-	aface INTEGER;
-	face GEOMETRY;
-	snodegeom GEOMETRY;
-	enodegeom GEOMETRY;
-	count INTEGER;
-	rec RECORD;
-	edgeid INTEGER;
-BEGIN
-
-	--
-	-- All arguments required
-	-- 
-	IF atopology IS NULL
-	   OR anode IS NULL
-	   OR anothernode IS NULL
-	   OR acurve IS NULL
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Acurve must be a LINESTRING
-	--
-	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - invalid curve'';
-	END IF;
-
-	--
-	-- Acurve must be a simple
-	--
-	IF NOT issimple(acurve)
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - curve not simple'';
-	END IF;
-
-	--
-	-- Check for:
-	--    existence of nodes
-	--    nodes faces match
-	-- Extract:
-	--    nodes face id
-	--    nodes geoms
-	--
-	aface := NULL;
-	count := 0;
-	FOR rec IN EXECUTE ''SELECT geom, containing_face, node_id FROM ''
-		|| quote_ident(atopology) || ''.node
-		WHERE node_id = '' || anode ||
-		'' OR node_id = '' || anothernode
-	LOOP 
-		IF count > 0 AND aface != rec.containing_face THEN
-			RAISE EXCEPTION
-			''SQL/MM Spatial exception - nodes in different faces'';
-		ELSE
-			aface := rec.containing_face;
-		END IF;
-
-		-- Get nodes geom
-		IF rec.node_id = anode THEN
-			snodegeom = rec.geom;
-		ELSE
-			enodegeom = rec.geom;
-		END IF;
-
-		count = count+1;
-
-	END LOOP;
-	IF count < 2 THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - non-existent node'';
-	END IF;
-
-
-	--
-	-- Check nodes isolation.
-	-- 
-	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
-		|| quote_ident(atopology) || ''.edge_data '' ||
-		'' WHERE start_node =  '' || anode ||
-		'' OR end_node = '' || anode ||
-		'' OR start_node = '' || anothernode ||
-		'' OR end_node = '' || anothernode
-	LOOP
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - not isolated node'';
-	END LOOP;
-
-	--
-	-- Check acurve to be within endpoints containing face 
-	-- (unless it is the world face, I suppose)
-	-- 
-	IF aface IS NOT NULL THEN
-
-		--
-		-- Extract endpoints face geometry
-		--
-		FOR rec IN EXECUTE ''SELECT topology.ST_GetFaceGeometry(''
-			|| quote_literal(atopology) ||
-			'','' || aface || '') as face''
-		LOOP
-			face := rec.face;
-		END LOOP;
-
-		--
-		-- Check acurve to be within face
-		--
-		IF ! within(acurve, face) THEN
-	RAISE EXCEPTION
-	''SQL/MM Spatial exception ­ geometry not within face.'';
-		END IF;
-
-	END IF;
-
-	--
-	-- l) Check that start point of acurve match start node
-	-- geoms.
-	-- 
-	IF x(snodegeom) != x(StartPoint(acurve)) OR
-	   y(snodegeom) != y(StartPoint(acurve)) THEN
-  RAISE EXCEPTION
-  ''SQL/MM Spatial exception ­ start node not geometry start point.'';
-	END IF;
-
-	--
-	-- m) Check that end point of acurve match end node
-	-- geoms.
-	-- 
-	IF x(enodegeom) != x(EndPoint(acurve)) OR
-	   y(enodegeom) != y(EndPoint(acurve)) THEN
-  RAISE EXCEPTION
-  ''SQL/MM Spatial exception ­ end node not geometry end point.'';
-	END IF;
-
-	--
-	-- n) Check if curve crosses (contains) any node
-	-- I used _contains_ here to leave endpoints out
-	-- 
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node ''
-		|| '' WHERE geom && '' || quote_literal(acurve) 
-		|| '' AND contains('' || quote_literal(acurve)
-		|| '',geom)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry crosses a node'';
-	END LOOP;
-
-	--
-	-- o) Check if curve intersects any other edge
-	-- 
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data
-		WHERE geom && '' || quote_literal(acurve) || ''::geometry
-		AND intersects(geom, '' || quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry intersects an edge'';
-	END LOOP;
-
-	--
-	-- Get new edge id from sequence
-	--
-	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
-		atopology || ''.edge_data_edge_id_seq'''')''
-	LOOP
-		edgeid = rec.nextval;
-	END LOOP;
-
-	--
-	-- Insert the new row
-	--
-	IF aface IS NULL THEN aface := 0; END IF;
-
-	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
-		|| ''.edge VALUES(''||edgeid||'',''||anode||
-			'',''||anothernode||'',''
-			||(-edgeid)||'',''||edgeid||'',''
-			||aface||'',''||aface||'',''
-			||quote_literal(acurve)||'')'';
-
-	RETURN edgeid;
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddIsoEdge
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.6
---
---  ST_ChangeEdgeGeom(atopology, anedge, acurve)
--- 
-CREATEFUNCTION topology.ST_ChangeEdgeGeom(varchar, integer, geometry)
-	RETURNS TEXT AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anedge ALIAS FOR $2;
-	acurve ALIAS FOR $3;
-	aface INTEGER;
-	face GEOMETRY;
-	snodegeom GEOMETRY;
-	enodegeom GEOMETRY;
-	count INTEGER;
-	rec RECORD;
-	edgeid INTEGER;
-	oldedge RECORD;
-BEGIN
-
-	--
-	-- All arguments required
-	-- 
-	IF atopology IS NULL
-	   OR anedge IS NULL
-	   OR acurve IS NULL
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Acurve must be a LINESTRING
-	--
-	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - invalid curve'';
-	END IF;
-
-	--
-	-- Acurve must be a simple
-	--
-	IF NOT issimple(acurve)
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - curve not simple'';
-	END IF;
-
-	--
-	-- e) Check StartPoint consistency
-	--
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data e, ''
-		|| quote_ident(atopology) || ''.node n ''
-		|| '' WHERE e.edge_id = '' || anedge
-		|| '' AND n.node_id = e.start_node ''
-		|| '' AND ( x(n.geom) != ''
-		|| x(StartPoint(acurve))
-		|| '' OR y(n.geom) != ''
-		|| y(StartPoint(acurve))
-		|| '')''
-	LOOP 
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - start node not geometry start point.'';
-	END LOOP;
-
-	--
-	-- f) Check EndPoint consistency
-	--
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data e, ''
-		|| quote_ident(atopology) || ''.node n ''
-		|| '' WHERE e.edge_id = '' || anedge
-		|| '' AND n.node_id = e.end_node ''
-		|| '' AND ( x(n.geom) != ''
-		|| x(EndPoint(acurve))
-		|| '' OR y(n.geom) != ''
-		|| y(EndPoint(acurve))
-		|| '')''
-	LOOP 
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - end node not geometry end point.'';
-	END LOOP;
-
-	--
-	-- g) Check if curve crosses any node
-	-- _within_ used to let endpoints out
-	-- 
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node
-		WHERE geom && '' || quote_literal(acurve) || ''::geometry
-		AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry crosses a node'';
-	END LOOP;
-
-	--
-	-- h) Check if curve intersects any other edge
-	-- 
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data ''
-		|| '' WHERE edge_id != '' || anedge
-		|| '' AND geom && ''
-		|| quote_literal(acurve) || ''::geometry ''
-		|| '' AND intersects(geom, ''
-		|| quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry intersects an edge'';
-	END LOOP;
-
-	--
-	-- Update edge geometry
-	--
-	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
-		|| '' SET geom = '' || quote_literal(acurve) 
-		|| '' WHERE edge_id = '' || anedge;
-
-	RETURN ''Edge '' || anedge || '' changed'';
-
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_ChangeEdgeGeom
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.12
---
---  ST_AddEdgeNewFaces(atopology, anode, anothernode, acurve)
---
-CREATEFUNCTION topology.ST_AddEdgeNewFaces(varchar, integer, integer, geometry)
-	RETURNS INTEGER AS
-'
-DECLARE
-	atopology ALIAS FOR $1;
-	anode ALIAS FOR $2;
-	anothernode ALIAS FOR $3;
-	acurve ALIAS FOR $4;
-	rec RECORD;
-	i INTEGER;
-	az FLOAT8;
-	azdif FLOAT8;
-	myaz FLOAT8;
-	minazimuth FLOAT8;
-	maxazimuth FLOAT8;
-	p2 GEOMETRY;
-BEGIN
-
-	--
-	-- All args required
-	-- 
-	IF atopology IS NULL
-		OR anode IS NULL
-		OR anothernode IS NULL
-		OR acurve IS NULL
-	THEN
-		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	--
-	-- Acurve must be a LINESTRING
-	--
-	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
-	THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - invalid curve'';
-	END IF;
-	
-	--
-	-- Curve must be simple
-	--
-	IF NOT issimple(acurve) THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - curve not simple'';
-	END IF;
-
-	-- 
-	-- Check endpoints existance and match with Curve geometry
-	--
-	i=0;
-	FOR rec IN EXECUTE ''SELECT ''
-		|| '' CASE WHEN node_id = '' || anode
-		|| '' THEN 1 WHEN node_id = '' || anothernode
-		|| '' THEN 0 END AS start, geom FROM ''
-		|| quote_ident(atopology)
-		|| ''.node ''
-		|| '' WHERE node_id IN ( ''
-		|| anode || '','' || anothernode
-		|| '')''
-	LOOP
-		IF rec.start THEN
-			IF NOT Equals(rec.geom, StartPoint(acurve))
-			THEN
-	RAISE EXCEPTION
-	''SQL/MM Spatial exception - start node not geometry start point.'';
-			END IF;
-		ELSE
-			IF NOT Equals(rec.geom, EndPoint(acurve))
-			THEN
-	RAISE EXCEPTION
-	''SQL/MM Spatial exception - end node not geometry end point.'';
-			END IF;
-		END IF;
-
-		i=i+1;
-	END LOOP;
-
-	IF i < 2 THEN
-		RAISE EXCEPTION
-		 ''SQL/MM Spatial exception - non-existent node'';
-	END IF;
-
-	--
-	-- Check if this geometry crosses any node
-	--
-	FOR rec IN EXECUTE ''SELECT node_id FROM ''
-		|| quote_ident(atopology) || ''.node
-		WHERE geom && '' || quote_literal(acurve) || ''::geometry
-		AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry crosses a node'';
-	END LOOP;
-
-	--
-	-- Check if this geometry crosses any existing edge
-	--
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data
-		WHERE geom && '' || quote_literal(acurve) || ''::geometry
-		AND crosses(geom, '' || quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - geometry crosses an edge'';
-	END LOOP;
-
-	--
-	-- Check if another edge share this edge endpoints
-	--
-	FOR rec IN EXECUTE ''SELECT * FROM ''
-		|| quote_ident(atopology) || ''.edge_data ''
-		|| '' WHERE ''
-		|| '' geom && '' || quote_literal(acurve) || ''::geometry ''
-		|| '' AND ''
-		|| '' ( (''
-		|| '' start_node = '' || anode
-		|| '' AND ''
-		|| '' end_node = '' || anothernode
-		|| '' ) OR ( ''
-		|| '' end_node = '' || anode
-		|| '' AND ''
-		|| '' start_node = '' || anothernode
-		|| '' ) )''
-		|| '' AND ''
-		|| ''equals(geom,'' || quote_literal(acurve) || ''::geometry)''
-	LOOP
-		RAISE EXCEPTION
-		''SQL/MM Spatial exception - coincident edge'';
-	END LOOP;
-
-	---------------------------------------------------------------
-	--
-	-- All checks passed, time to extract informations about
-	-- endpoints:
-	--
-	--      next_left_edge
-	--      next_right_edge
-	--	left_face
-	--	right_face
-	--
-	---------------------------------------------------------------
-
-	--
-	--
-	-- Compute next_left_edge 
-	--
-	-- We fetch all edges with an endnode equal to
-	-- this edge end_node (anothernode).
-	-- For each edge we compute azimuth of the segment(s).
-	-- Of interest are the edges with closest (smaller
-	-- and bigger) azimuths then the azimuth of
-	-- this edge last segment.
-	--
-
-	myaz = azimuth(EndPoint(acurve), PointN(acurve, NumPoints(acurve)-1));
-	RAISE NOTICE ''My end-segment azimuth: %'', myaz;
-	FOR rec IN EXECUTE ''SELECT ''
-		|| ''edge_id, end_node, start_node, geom''
-		|| '' FROM ''
-		|| quote_ident(atopology)
-		|| ''.edge_data ''
-		|| '' WHERE ''
-		|| '' end_node = '' || anothernode
-		|| '' OR ''
-		|| '' start_node = '' || anothernode
-	LOOP
-
-		IF rec.start_node = anothernode THEN
-			--
-			-- Edge starts at our node, we compute
-			-- azimuth from node to its second point
-			--
-			az = azimuth(EndPoint(acurve),
-				PointN(rec.geom, 2));
-
-			RAISE NOTICE ''Edge % starts at node % - azimuth %'',
-				rec.edge_id, rec.start_node, az;
-		END IF;
-
-		IF rec.end_node = anothernode THEN
-			--
-			-- Edge ends at our node, we compute
-			-- azimuth from node to its second-last point
-			--
-			az = azimuth(EndPoint(acurve),
-				PointN(rec.geom, NumPoints(rec.geom)-1));
-
-			RAISE NOTICE ''Edge % ends at node % - azimuth %'',
-				rec.edge_id, rec.end_node, az;
-		END IF;
-	END LOOP;
-
-
-	RAISE EXCEPTION ''Not implemented yet'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_AddEdgeNewFaces
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.17
---
---  ST_InitTopoGeo(atopology)
---
-CREATEFUNCTION topology.ST_InitTopoGeo(varchar)
-RETURNS text
-AS '
-DECLARE
-	atopology alias for $1;
-	rec RECORD;
-	topology_id numeric;
-BEGIN
-	IF atopology IS NULL THEN
-		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
-	LOOP
-		RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
-	END LOOP;
-
-	FOR rec IN EXECUTE ''SELECT topology.CreateTopology(''
-		||quote_literal(atopology)|| '') as id''
-	LOOP
-		topology_id := rec.id;
-	END LOOP;
-
-	RETURN ''Topology-Geometry '' || quote_literal(atopology)
-		|| '' (id:'' || topology_id || '') created. '';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_InitTopoGeo
-
---{
--- Topo-Geo and Topo-Net 3: Routine Details
--- X.3.18
---
---  ST_CreateTopoGeo(atopology, acollection)
---
-CREATEFUNCTION topology.ST_CreateTopoGeo(varchar, geometry)
-RETURNS text
-AS '
-DECLARE
-	atopology alias for $1;
-	acollection alias for $2;
-	typ char(4);
-	rec RECORD;
-	ret int;
-	schemaoid oid;
-BEGIN
-	IF atopology IS NULL OR acollection IS NULL THEN
-		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
-	END IF;
-
-	-- Verify existance of the topology schema 
-	FOR rec in EXECUTE ''SELECT oid,count(*) FROM pg_namespace WHERE ''
-		|| '' nspname = '' || quote_literal(atopology)
-		|| '' GROUP BY oid''
-		
-	LOOP
-		IF rec.count < 1 THEN
-	RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent schema'';
-		END IF;
-		schemaoid := rec.oid;
-	END LOOP;
-
-	-- Verify existance of the topology views in the topology schema 
-	FOR rec in EXECUTE ''SELECT count(*) FROM pg_class WHERE ''
-		|| '' relnamespace = '' || schemaoid 
-		|| '' and relname = ''''node''''''
-		|| '' OR relname = ''''edge''''''
-		|| '' OR relname = ''''face''''''
-	LOOP
-		IF rec.count < 3 THEN
-	RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent view'';
-		END IF;
-	END LOOP;
-
-	-- Verify the topology views in the topology schema to be empty
-	FOR rec in EXECUTE
-		''SELECT count(*) FROM ''
-		|| quote_ident(atopology) || ''.edge_data ''
-		|| '' UNION '' ||
-		''SELECT count(*) FROM ''
-		|| quote_ident(atopology) || ''.node ''
-	LOOP
-		IF rec.count > 0 THEN
-	RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty view'';
-		END IF;
-	END LOOP;
-
-	-- face check is separated as it will contain a single (world)
-	-- face record
-	FOR rec in EXECUTE
-		''SELECT count(*) FROM ''
-		|| quote_ident(atopology) || ''.face ''
-	LOOP
-		IF rec.count != 1 THEN
-	RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty face view'';
-		END IF;
-	END LOOP;
-
-	-- 
-	-- LOOP through the elements invoking the specific function
-	-- 
-	FOR rec IN SELECT geom(dump(acollection))
-	LOOP
-		typ := substring(geometrytype(rec.geom), 1, 3);
-
-		IF typ = ''LIN'' THEN
-	SELECT topology.TopoGeo_addLinestring(atopology, rec.geom) INTO ret;
-		ELSIF typ = ''POI'' THEN
-	SELECT topology.TopoGeo_AddPoint(atopology, rec.geom) INTO ret;
-		ELSIF typ = ''POL'' THEN
-	SELECT topology.TopoGeo_AddPolygon(atopology, rec.geom) INTO ret;
-		ELSE
-	RAISE EXCEPTION ''ST_CreateTopoGeo got unknown geometry type: %'', typ;
-		END IF;
-
-	END LOOP;
-
-	RETURN ''Topology '' || atopology || '' populated'';
-
-	RAISE EXCEPTION ''ST_CreateTopoGeo not implemente yet'';
-END
-'
-LANGUAGE 'plpgsql' _VOLATILE;
---} ST_CreateTopoGeo
-
---=}  SQL/MM block
-
-COMMIT;
-

Added: trunk/topology/topology.sql.in.c
===================================================================
--- trunk/topology/topology.sql.in.c	2008-05-20 22:24:09 UTC (rev 2778)
+++ trunk/topology/topology.sql.in.c	2008-05-22 14:31:02 UTC (rev 2779)
@@ -0,0 +1,3808 @@
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+-- 
+-- $Id: topology.sql.in 2469 2006-09-06 11:16:59Z strk $
+--
+-- PostGIS - Spatial Types for PostgreSQL
+-- http://postgis.refractions.net
+-- Copyright 2005 Refractions Research Inc.
+--
+-- This is free software; you can redistribute and/or modify it under
+-- the terms of the GNU General Public Licence. See the COPYING file.
+--
+-- Author: Sandro Santilli <strk at refractions.net>
+--  
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+-- STATUS:
+--
+--	All objects are created in the 'topology' schema.
+--
+--	We have PostGIS-specific objects and SQL/MM objects.
+--	PostGIS-specific objects have no prefix, SQL/MM ones
+--	have the ``ST_'' prefix.
+--	
+-- [PostGIS-specific]
+--
+--   TABLE topology
+--	Table storing topology info (name, srid, precision)
+--
+--   TYPE TopoGeometry
+--	Complex type storing topology_id, layer_id, geometry type
+--	and topogeometry id.
+--
+--   DOMAIN TopoElement
+--	An array of two elements: element_id and element_type.
+--	In fact, an array of integers.
+--
+--   DOMAIN TopoElementArray
+--	An array of element_id,element_type values.
+--	In fact, a bidimensional array of integers:
+--	'{{id,type}, {id,type}, ...}'
+--
+--   FUNCTION CreateTopology(name, [srid], [precision])
+--	Initialize a new topology (creating schema with
+--	edge,face,node,relation) and add a record into
+--	the topology.topology table.
+--	TODO: add triggers (or rules, or whatever) enforcing
+--	      precision to the edge and node tables.
+--  
+--   FUNCTION DropTopology(name)
+--  	Delete a topology removing reference from the
+--  	topology.topology table
+--
+--   FUNCTION GetTopologyId(name)
+--   FUNCTION GetTopologyName(id)
+--	Return info about a Topology
+--
+--   FUNCTION AddTopoGeometryColumn(toponame, schema, table, column, geomtype)
+--      Add a TopoGeometry column to a table, making it a topology layer.
+--      Returns created layer id.
+--
+--   FUNCTION DropTopoGeometryColumn(schema, table, column)
+--  	Drop a TopoGeometry column, unregister the associated layer,
+-- 	cleanup the relation table.
+--
+--   FUNCTION CreateTopoGeom(toponame, geomtype, layer_id, topo_objects)
+--  	Create a TopoGeometry object from existing Topology elements.
+--	The "topo_objects" parameter is of TopoElementArray type.
+--
+--   FUNCTION GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
+--   FUNCTION GetTopoGeomElementArray(TopoGeometry)
+--  	Returns a TopoElementArray object containing the topological
+--	elements of the given TopoGeometry.
+--
+--   FUNCTION GetTopoGeomElements(toponame, layer_id, topogeom_id)
+--   FUNCTION GetTopoGeomElements(TopoGeometry)
+--  	Returns a set of TopoElement objects containing the
+--	topological elements of the given TopoGeometry (primitive
+--	elements)
+--
+--   FUNCTION ValidateTopology(toponame)
+--  	Run validity checks on the topology, returning, for each
+--	detected error, a 3-columns row containing error string
+--	and references to involved topo elements: error, id1, id2
+--
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+--  Overloaded functions for TopoGeometry inputs
+--
+--   FUNCTION intersects(TopoGeometry, TopoGeometry)
+--   FUNCTION equals(TopoGeometry, TopoGeometry)
+--
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+--   FUNCTION TopoGeo_AddPoint(toponame, point)
+--	Add a Point geometry to the topology
+--	TODO: accept a topology/layer id
+--	      rework to use existing node if existent
+--
+--   FUNCTION TopoGeo_AddLinestring(toponame, line)
+--	Add a LineString geometry to the topology
+--	TODO: accept a topology/layer id
+--	      rework to use existing nodes/edges
+--	      splitting them if required
+--
+--   FUNCTION TopoGeo_AddPolygon(toponame, polygon)
+--	Add a Polygon geometry to the topology
+--	TODO: implement
+--
+--   TYPE GetFaceEdges_ReturnType
+--	Complex type used to return tuples from ST_GetFaceEdges
+--
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+-- [SQL/MM]
+--
+--   ST_InitTopoGeo
+--  	Done, can be modified to include explicit sequences or
+--  	more constraints. Very noisy due to implicit index creations
+--  	for primary keys and sequences for serial fields...
+--  
+--   ST_CreateTopoGeo
+--  	Being working on. TODO: continue implementation
+--  
+--   ST_AddIsoNode
+--  	Complete
+--  
+--   ST_RemoveIsoNode
+--  	Complete
+--  
+--   ST_MoveIsoNode
+--  	Complete
+--  
+--   ST_AddIsoEdge
+--  	Complete
+--
+--   ST_RemoveIsoEdge
+--	Complete, exceptions untested
+--  
+--   ST_ChangeEdgeGeom
+--  	Complete
+--
+--   ST_NewEdgesSplit
+--	Complete
+--	this also updates the Relation table
+--	TODO: add entries to the History table ?
+--
+--   ST_ModEdgesSplit
+--	Complete
+--	this also updates the Relation table
+--	TODO: add entries to the History table ?
+--
+--   ST_AddEdgeNewFaces
+--	Being working on. TODO: continue
+--  
+--   ST_GetFaceEdges
+--  	Unimplemented (C seems appropriate)
+--  
+--   ST_GetFaceGeometry
+--  	Implemented using polygonize()
+--  
+--  
+
+#include "../lwgeom/sqldefines.h"
+
+DROP SCHEMA topology CASCADE;
+
+BEGIN;
+
+CREATE SCHEMA topology;
+
+--={ ----------------------------------------------------------------
+--  POSTGIS-SPECIFIC block
+--
+--  This part contains function NOT in the SQL/MM specification
+--
+---------------------------------------------------------------------
+
+--
+-- Topology table.
+-- Stores id,name,precision and SRID of topologies.
+--
+CREATE TABLE topology.topology (
+	id SERIAL NOT NULL PRIMARY KEY,
+	name VARCHAR NOT NULL UNIQUE,
+	SRID INTEGER NOT NULL,
+	precision FLOAT8 NOT NULL
+);
+
+--{ LayerTrigger()
+--
+-- Layer integrity trigger
+--
+CREATEFUNCTION topology.LayerTrigger()
+	RETURNS trigger
+AS
+'
+DECLARE
+	rec RECORD;
+	ok BOOL;
+	toponame varchar;
+	query TEXT;
+BEGIN
+
+	--RAISE NOTICE ''LayerTrigger called % % at % level'', TG_WHEN, TG_OP, TG_LEVEL;
+
+
+	IF TG_OP = ''INSERT'' THEN
+		RAISE EXCEPTION ''LayerTrigger not meant to be called on INSERT'';
+	ELSIF TG_OP = ''UPDATE'' THEN
+		RAISE EXCEPTION ''The topology.layer table cannot be updated'';
+	END IF;
+
+
+	-- Check for existance of any feature column referencing
+	-- this layer
+	FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
+		WHERE text(n.nspname) = OLD.schema_name
+		AND c.relnamespace = n.oid
+		AND text(c.relname) = OLD.table_name
+		AND a.attrelid = c.oid
+		AND text(a.attname) = OLD.feature_column
+	LOOP
+		query = ''SELECT * ''
+			|| '' FROM '' || quote_ident(OLD.schema_name)
+			|| ''.'' || quote_ident(OLD.table_name)
+			|| '' WHERE layer_id(''
+			|| quote_ident(OLD.feature_column)||'') ''
+			|| ''='' || OLD.layer_id
+			|| '' LIMIT 1'';
+		--RAISE NOTICE ''%'', query;
+		FOR rec IN EXECUTE query
+		LOOP
+			RAISE NOTICE ''A feature referencing layer % of topology % still exists in %.%.%'', OLD.layer_id, OLD.topology_id, OLD.schema_name, OLD.table_name, OLD.feature_column;
+			RETURN NULL;
+		END LOOP;
+	END LOOP;
+
+
+	-- Get topology name
+	SELECT name FROM topology.topology INTO toponame
+		WHERE id = OLD.topology_id;
+
+	IF toponame IS NULL THEN
+		RAISE NOTICE ''Could not find name of topology with id %'',
+			OLD.layer_id;
+	END IF;
+
+	-- Check if any record in the relation table references this layer
+	FOR rec IN SELECT * FROM pg_namespace
+		WHERE text(nspname) = toponame
+	LOOP
+		query = ''SELECT * ''
+			|| '' FROM '' || quote_ident(toponame)
+			|| ''.relation ''
+			|| '' WHERE topogeo_id = '' || OLD.topology_id 
+			|| '' AND layer_id = ''|| OLD.layer_id
+			|| '' LIMIT 1'';
+		--RAISE NOTICE ''%'', query;
+		FOR rec IN EXECUTE query
+		LOOP
+			RAISE NOTICE ''A record in %.relation still references layer %'', toponame, OLD.layer_id;
+			RETURN NULL;
+		END LOOP;
+	END LOOP;
+
+	RETURN OLD;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} LayerTrigger()
+
+
+--{
+-- Layer table.
+-- Stores topology layer informations
+--
+CREATE TABLE topology.layer (
+	topology_id INTEGER NOT NULL
+		REFERENCES topology.topology(id),
+	layer_id integer NOT NULL,
+	schema_name VARCHAR NOT NULL,
+	table_name VARCHAR NOT NULL,
+	feature_column VARCHAR NOT NULL,
+	feature_type VARCHAR NOT NULL,
+	level INTEGER NOT NULL DEFAULT 0,
+	child_id INTEGER DEFAULT NULL,
+	UNIQUE(schema_name, table_name, feature_column),
+	PRIMARY KEY(topology_id, layer_id)
+);
+
+CREATE TRIGGER layer_integrity_checks BEFORE UPDATE OR DELETE
+ON topology.layer FOR EACH ROW EXECUTE PROCEDURE topology.LayerTrigger();
+
+--} Layer table.
+
+--
+-- Type returned by ST_GetFaceEdges
+--
+CREATE TYPE topology.GetFaceEdges_ReturnType AS (
+	sequence integer,
+	edge integer
+);
+
+--
+-- Type returned by ValidateTopology 
+--
+CREATE TYPE topology.ValidateTopology_ReturnType AS (
+	error varchar,
+	id1 integer,
+	id2 integer
+);
+
+--
+-- TopoGeometry type
+--
+CREATE TYPE topology.TopoGeometry AS (
+	topology_id integer,
+	layer_id integer,
+	id integer,
+	type integer -- 1: [multi]point, 2: [multi]line,
+	             -- 3: [multi]polygon, 4: collection
+);
+
+--
+-- TopoElement domain
+--
+CREATE DOMAIN topology.TopoElement AS integer[]
+	CONSTRAINT DIMENSIONS CHECK (
+		array_upper(VALUE, 2) IS NULL
+		AND array_upper(VALUE, 1) = 2
+	);
+--
+-- TopoElementArray domain
+--
+CREATE DOMAIN topology.TopoElementArray AS integer[][]
+	CONSTRAINT DIMENSIONS CHECK (
+		array_upper(VALUE, 2) IS NOT NULL
+		AND array_upper(VALUE, 2) = 2
+		AND array_upper(VALUE, 3) IS NULL
+	);
+
+--
+-- TopoGeomElementArray domain
+--
+CREATE DOMAIN topology.TopoGeomElementArray AS integer[][]
+	CONSTRAINT DIMENSIONS CHECK (
+		array_upper(VALUE, 2) IS NOT NULL
+		AND array_upper(VALUE, 2) = 2
+		AND array_upper(VALUE, 3) IS NULL
+	);
+
+--{ RelationTrigger()
+--
+-- Relation integrity trigger
+--
+CREATEFUNCTION topology.RelationTrigger()
+	RETURNS trigger
+AS
+'
+DECLARE
+	toponame varchar;
+	topoid integer;
+	plyr RECORD; -- parent layer
+	rec RECORD;
+	ok BOOL;
+
+BEGIN
+	IF TG_NARGS != 2 THEN
+		RAISE EXCEPTION ''RelationTrigger called with wrong number of arguments'';
+	END IF;
+
+	topoid = TG_ARGV[0];
+	toponame = TG_ARGV[1];
+
+	--RAISE NOTICE ''RelationTrigger called % % on %.relation for a %'', TG_WHEN, TG_OP, toponame, TG_LEVEL;
+
+
+	IF TG_OP = ''DELETE'' THEN
+		RAISE EXCEPTION ''RelationTrigger not meant to be called on DELETE'';
+	END IF;
+
+	-- Get layer info (and verify it exists)
+	ok = false;
+	FOR plyr IN EXECUTE ''SELECT * FROM topology.layer ''
+		|| ''WHERE ''
+		|| '' topology_id = '' || topoid
+		|| '' AND''
+		|| '' layer_id = '' || NEW.layer_id
+	LOOP
+		ok = true;
+		EXIT;
+	END LOOP;
+	IF NOT ok THEN
+		RAISE EXCEPTION ''Layer % does not exist in topology %'',
+			NEW.layer_id, topoid;
+		RETURN NULL;
+	END IF;
+
+	IF plyr.level > 0 THEN -- this is hierarchical layer
+
+		-- ElementType must be the layer child id
+		IF NEW.element_type != plyr.child_id THEN
+			RAISE EXCEPTION ''Type of elements in layer % must be set to its child layer id %'', plyr.layer_id, plyr.child_id;
+			RETURN NULL;
+		END IF;
+
+		-- ElementId must be an existent TopoGeometry in child layer
+		ok = false;
+		FOR rec IN EXECUTE ''SELECT topogeo_id FROM ''
+			|| quote_ident(toponame) || ''.relation ''
+			|| '' WHERE layer_id = '' || plyr.child_id 
+			|| '' AND topogeo_id = '' || NEW.element_id
+		LOOP
+			ok = true;
+			EXIT;
+		END LOOP;
+		IF NOT ok THEN
+			RAISE EXCEPTION ''TopoGeometry % does not exist in the child layer %'', NEW.element_id, plyr.child_id;
+			RETURN NULL;
+		END IF;
+
+	ELSE -- this is a basic layer
+
+		-- ElementType must be compatible with layer type
+		IF plyr.feature_type != 4
+			AND plyr.feature_type != NEW.element_type
+		THEN
+			RAISE EXCEPTION ''Element of type % is not compatible with layer of type %'', NEW.element_type, plyr.feature_type;
+			RETURN NULL;
+		END IF;
+
+		--
+		-- Now lets see if the element is consistent, which
+		-- is it exists in the topology tables.
+		--
+
+		--
+		-- Element is a Node
+		--
+		IF NEW.element_type = 1 
+		THEN
+			ok = false;
+			FOR rec IN EXECUTE ''SELECT node_id FROM ''
+				|| quote_ident(toponame) || ''.node ''
+				|| '' WHERE node_id = '' || NEW.element_id
+			LOOP
+				ok = true;
+				EXIT;
+			END LOOP;
+			IF NOT ok THEN
+				RAISE EXCEPTION ''Node % does not exist in topology %'', NEW.element_id, toponame;
+				RETURN NULL;
+			END IF;
+
+		--
+		-- Element is an Edge
+		--
+		ELSIF NEW.element_type = 2 
+		THEN
+			ok = false;
+			FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+				|| quote_ident(toponame) || ''.edge_data ''
+				|| '' WHERE edge_id = '' || abs(NEW.element_id)
+			LOOP
+				ok = true;
+				EXIT;
+			END LOOP;
+			IF NOT ok THEN
+				RAISE EXCEPTION ''Edge % does not exist in topology %'', NEW.element_id, toponame;
+				RETURN NULL;
+			END IF;
+
+		--
+		-- Element is a Face
+		--
+		ELSIF NEW.element_type = 3 
+		THEN
+			IF NEW.element_id = 0 THEN
+				RAISE EXCEPTION ''Face % cannot be associated with any feature'', NEW.element_id;
+				RETURN NULL;
+			END IF;
+			ok = false;
+			FOR rec IN EXECUTE ''SELECT face_id FROM ''
+				|| quote_ident(toponame) || ''.face ''
+				|| '' WHERE face_id = '' || NEW.element_id
+			LOOP
+				ok = true;
+				EXIT;
+			END LOOP;
+			IF NOT ok THEN
+				RAISE EXCEPTION ''Face % does not exist in topology %'', NEW.element_id, toponame;
+				RETURN NULL;
+			END IF;
+		END IF;
+
+	END IF;
+	
+	RETURN NEW;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} RelationTrigger()
+
+--{
+--  AddTopoGeometryColumn(toponame, schema, table, colum, type, [child])
+--
+--  Add a TopoGeometry column to a table, making it a topology layer.
+--  Returns created layer id.
+--
+--
+CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar, integer)
+	RETURNS integer
+AS '
+DECLARE
+	toponame alias for $1;
+	schema alias for $2;
+	table alias for $3;
+	col alias for $4;
+	ltype alias for $5;
+	child alias for $6;
+	intltype integer;
+	level integer;
+	topoid integer;
+	rec RECORD;
+	layer_id integer;
+	query text;
+BEGIN
+
+        -- Get topology id
+        SELECT id FROM topology.topology into topoid
+                WHERE name = toponame;
+
+	IF topoid IS NULL THEN
+		RAISE EXCEPTION ''Topology % does not exist'', toponame;
+	END IF;
+
+	--
+	-- Get new layer id from sequence
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		quote_ident(toponame) || ''.layer_id_seq'''')''
+	LOOP
+		layer_id = rec.nextval;
+	END LOOP;
+
+	IF ltype = ''POINT'' THEN
+		intltype = 1;
+	ELSIF ltype = ''LINE'' THEN
+		intltype = 2;
+	ELSIF ltype = ''POLYGON'' THEN
+		intltype = 3;
+	ELSIF ltype = ''COLLECTION'' THEN
+		intltype = 4;
+	ELSE
+		RAISE EXCEPTION ''Layer type must be one of POINT,LINE,POLYGON,COLLECTION'';
+	END IF;
+
+	--
+	-- See if child id exists and extract its level
+	--
+	IF child IS NULL THEN
+		EXECUTE ''INSERT INTO ''
+			|| ''topology.layer(topology_id, ''
+			|| ''layer_id, schema_name, ''
+			|| ''table_name, feature_column, feature_type) ''
+			|| ''VALUES (''
+			|| topoid || '',''
+			|| layer_id || '','' 
+			|| quote_literal(schema) || '',''
+			|| quote_literal(table) || '',''
+			|| quote_literal(col) || '',''
+			|| intltype || '');'';
+	ELSE
+		FOR rec IN EXECUTE ''SELECT level FROM topology.layer''
+			|| '' WHERE layer_id = '' || child
+		LOOP
+			level = rec.level + 1;
+		END LOOP;
+
+		EXECUTE ''INSERT INTO '' 
+			|| ''topology.layer(topology_id, ''
+			|| ''layer_id, level, child_id, schema_name, ''
+			|| ''table_name, feature_column, feature_type) ''
+			|| ''VALUES (''
+			|| topoid || '',''
+			|| layer_id || '','' || level || '',''
+			|| child || '',''
+			|| quote_literal(schema) || '',''
+			|| quote_literal(table) || '',''
+			|| quote_literal(col) || '',''
+			|| intltype || '');'';
+	END IF;
+
+
+	--
+	-- Create a sequence for TopoGeometries in this new layer
+	--
+	EXECUTE ''CREATE SEQUENCE '' || quote_ident(toponame)
+		|| ''.topogeo_s_'' || layer_id;
+
+	--
+	-- Add new TopoGeometry column in schema.table
+	--
+	EXECUTE ''ALTER TABLE '' || quote_ident(schema)
+		|| ''.'' || quote_ident(table) 
+		|| '' ADD COLUMN '' || quote_ident(col)
+		|| '' topology.TopoGeometry;'';
+
+	--
+	-- Add constraints on TopoGeom column
+	--
+	EXECUTE ''ALTER TABLE '' || quote_ident(schema)
+		|| ''.'' || quote_ident(table) 
+		|| '' ADD CONSTRAINT check_topogeom CHECK (''
+		|| ''topology_id('' || quote_ident(col) || '') = '' || topoid
+		|| '' AND ''
+		|| ''layer_id('' || quote_ident(col) || '') = '' || layer_id
+		|| '' AND ''
+		|| ''type('' || quote_ident(col) || '') = '' || intltype
+		|| '');'';
+
+	--
+	-- Add dependency of the feature column on the topology schema
+	--
+	query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
+		|| ''fcat.oid, fobj.oid, fsub.attnum, tcat.oid, ''
+		|| ''tobj.oid, 0, ''''n'''' ''
+		|| ''FROM pg_class fcat, pg_namespace fnsp, ''
+		|| '' pg_class fobj, pg_attribute fsub, ''
+		|| '' pg_class tcat, pg_namespace tobj ''
+		|| '' WHERE fcat.relname = ''''pg_class'''' ''
+		|| '' AND fnsp.nspname = '' || quote_literal(schema)
+		|| '' AND fobj.relnamespace = fnsp.oid ''
+		|| '' AND fobj.relname = '' || quote_literal(table)
+		|| '' AND fsub.attrelid = fobj.oid ''
+		|| '' AND fsub.attname = '' || quote_literal(col)
+		|| '' AND tcat.relname = ''''pg_namespace'''' ''
+		|| '' AND tobj.nspname = '' || quote_literal(toponame);
+
+--
+-- The only reason to add this dependency is to avoid
+-- simple drop of a feature column. Still, drop cascade
+-- will remove both the feature column and the sequence
+-- corrupting the topology anyway ...
+--
+#if 0
+	--
+	-- Add dependency of the topogeom sequence on the feature column 
+	-- This is a dirty hack ...
+	--
+	query = ''INSERT INTO pg_catalog.pg_depend SELECT ''
+		|| ''scat.oid, sobj.oid, 0, fcat.oid, ''
+		|| ''fobj.oid, fsub.attnum, ''''n'''' ''
+		|| ''FROM pg_class fcat, pg_namespace fnsp, ''
+		|| '' pg_class fobj, pg_attribute fsub, ''
+		|| '' pg_class scat, pg_class sobj, ''
+		|| '' pg_namespace snsp ''
+		|| '' WHERE fcat.relname = ''''pg_class'''' ''
+		|| '' AND fnsp.nspname = '' || quote_literal(schema)
+		|| '' AND fobj.relnamespace = fnsp.oid ''
+		|| '' AND fobj.relname = '' || quote_literal(table)
+		|| '' AND fsub.attrelid = fobj.oid ''
+		|| '' AND fsub.attname = '' || quote_literal(col)
+		|| '' AND scat.relname = ''''pg_class'''' ''
+		|| '' AND snsp.nspname = '' || quote_literal(toponame)
+		|| '' AND sobj.relnamespace = snsp.oid '' 
+		|| '' AND sobj.relname = ''
+		|| '' ''''topogeo_s_'' || layer_id || '''''' '';
+
+	RAISE NOTICE ''%'', query;
+	EXECUTE query;
+#endif
+
+	RETURN layer_id;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+
+CREATEFUNCTION topology.AddTopoGeometryColumn(varchar, varchar, varchar, varchar, varchar)
+	RETURNS integer
+AS '
+	SELECT topology.AddTopoGeometryColumn($1, $2, $3, $4, $5, NULL);
+'
+LANGUAGE 'sql' _VOLATILE;
+
+--
+--} AddTopoGeometryColumn
+
+--{
+--  DropTopoGeometryColumn(schema, table, colum)
+--
+--  Drop a TopoGeometry column, unregister the associated layer,
+--  cleanup the relation table.
+--
+--
+CREATEFUNCTION topology.DropTopoGeometryColumn(varchar, varchar, varchar)
+	RETURNS text
+AS '
+DECLARE
+	schema alias for $1;
+	table alias for $2;
+	col alias for $3;
+	rec RECORD;
+	lyrinfo RECORD;
+	ok BOOL;
+	result text;
+BEGIN
+
+        -- Get layer and topology info
+	ok = false;
+	FOR rec IN EXECUTE ''SELECT t.name as toponame, l.* FROM ''
+		|| ''topology.topology t, topology.layer l ''
+		|| '' WHERE l.schema_name = '' || quote_literal(schema)
+		|| '' AND l.table_name = '' || quote_literal(table)
+		|| '' AND l.feature_column = '' || quote_literal(col)
+	LOOP
+		ok = true;
+		lyrinfo = rec;
+	END LOOP;
+
+	-- Layer not found
+	IF NOT ok THEN
+		RAISE EXCEPTION ''No layer registered on %.%.%'',
+			schema,table,col;
+	END IF;
+		
+	-- Clean up the topology schema
+	FOR rec IN SELECT * FROM pg_namespace
+		WHERE text(nspname) = lyrinfo.toponame
+	LOOP
+		-- Cleanup the relation table
+		EXECUTE ''DELETE FROM '' || quote_ident(lyrinfo.toponame)
+			|| ''.relation ''
+			|| '' WHERE ''
+			|| ''layer_id = '' || lyrinfo.layer_id;
+
+		-- Drop the sequence for topogeoms in this layer
+		EXECUTE ''DROP SEQUENCE '' || quote_ident(lyrinfo.toponame)
+			|| ''.topogeo_s_'' || lyrinfo.layer_id;
+
+	END LOOP;
+
+	ok = false;
+	FOR rec IN SELECT * FROM pg_namespace n, pg_class c, pg_attribute a
+		WHERE text(n.nspname) = schema
+		AND c.relnamespace = n.oid
+		AND text(c.relname) = table
+		AND a.attrelid = c.oid
+		AND text(a.attname) = col
+	LOOP
+		ok = true;
+		EXIT;
+	END LOOP;
+
+
+	IF ok THEN
+		-- Set feature column to NULL to bypass referential integrity
+		-- checks
+		EXECUTE ''UPDATE '' || quote_ident(schema) || ''.''
+			|| quote_ident(table)
+			|| '' SET '' || quote_ident(col)
+			|| '' = NULL'';
+	END IF;
+
+	-- Delete the layer record
+	EXECUTE ''DELETE FROM topology.layer ''
+		|| '' WHERE topology_id = '' || lyrinfo.topology_id
+		|| '' AND layer_id = '' || lyrinfo.layer_id;
+
+	IF ok THEN
+		-- Drop the layer column
+		EXECUTE ''ALTER TABLE '' || quote_ident(schema) || ''.''
+			|| quote_ident(table)
+			|| '' DROP '' || quote_ident(col)
+			|| '' cascade'';
+	END IF;
+
+	result = ''Layer '' || lyrinfo.layer_id || '' (''
+		|| schema || ''.'' || table || ''.'' || col
+		|| '') dropped'';
+
+	RETURN result;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--
+--} DropTopoGeometryColumn
+
+
+--{
+-- CreateTopoGeom(topology_name, topogeom_type, layer_id, elements)
+--
+-- Create a TopoGeometry object from Topology elements.
+-- The elements parameter is a two-dimensional array. 
+-- Every element of the array is either a Topology element represented by
+-- (id, type) or a TopoGeometry element represented by (id, layer).
+-- The actual semantic depends on the TopoGeometry layer, either at
+-- level 0 (elements are topological primitives) or higer (elements
+-- are TopoGeoms from child layer).
+-- 
+-- Return a topology.TopoGeometry object.
+--
+CREATEFUNCTION topology.CreateTopoGeom(varchar, integer, integer, topology.TopoElementArray)
+	RETURNS topology.TopoGeometry
+AS '
+DECLARE
+	toponame alias for $1;
+	tg_type alias for $2; -- 1:[multi]point
+	                      -- 2:[multi]line
+	                      -- 3:[multi]poly
+	                      -- 4:collection
+	layer_id alias for $3;
+	tg_objs alias for $4;
+	i integer;
+	dims varchar;
+	outerdims varchar;
+	innerdims varchar;
+	obj_type integer;
+	obj_id integer;
+	ret topology.TopoGeometry;
+	rec RECORD;
+	layertype integer;
+	layerlevel integer;
+	layerchild integer;
+BEGIN
+
+	IF tg_type < 1 OR tg_type > 4 THEN
+		RAISE EXCEPTION ''Invalid TopoGeometry type (must be in the range 1..4'';
+	END IF;
+
+	-- Get topology id into return TopoGeometry
+	SELECT id FROM topology.topology into ret.topology_id
+		WHERE name = toponame;
+
+	--
+	-- Get layer info
+	--
+	layertype := NULL;
+	FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
+		|| '' WHERE topology_id = '' || ret.topology_id
+		|| '' AND layer_id = '' || layer_id
+	LOOP
+		layertype = rec.feature_type;
+		layerlevel = rec.level;
+		layerchild = rec.child_id;
+	END LOOP;
+
+	-- Check for existence of given layer id
+	IF layertype IS NULL THEN
+		RAISE EXCEPTION ''No layer with id % is registered with topology %'', layer_id, toponame;
+	END IF;
+
+	-- Verify compatibility between layer geometry type and
+	-- TopoGeom requested geometry type
+	IF layertype != 4 and layertype != tg_type THEN
+		RAISE EXCEPTION ''A Layer of type % cannot contain a TopoGeometry of type %'', layertype, tg_type;
+	END IF;
+
+	-- Set layer id and type in return object
+	ret.layer_id = layer_id;
+	ret.type = tg_type;
+
+	--
+	-- Get new TopoGeo id from sequence
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		toponame || ''.topogeo_s_'' || layer_id || '''''')''
+	LOOP
+		ret.id = rec.nextval;
+	END LOOP;
+
+	-- Loop over outer dimension
+	i = array_lower(tg_objs, 1);
+	LOOP
+		obj_id = tg_objs[i][1];
+		obj_type = tg_objs[i][2];
+		IF layerlevel = 0 THEN -- array specifies lower-level objects
+			IF tg_type != 4 and tg_type != obj_type THEN
+				RAISE EXCEPTION ''A TopoGeometry of type % cannot contain topology elements of type %'', tg_type, obj_type;
+			END IF;
+		ELSE -- array specifies lower-level topogeometries
+			IF obj_type != layerchild THEN
+				RAISE EXCEPTION ''TopoGeom element layer do not match TopoGeom child layer'';
+			END IF;
+			-- TODO: verify that the referred TopoGeometry really
+			-- exists in the relation table ?
+		END IF;
+
+		--RAISE NOTICE ''obj:% type:% id:%'', i, obj_type, obj_id;
+
+		--
+		-- Insert record into the Relation table
+		--
+		EXECUTE ''INSERT INTO ''||quote_ident(toponame)
+			|| ''.relation(topogeo_id, layer_id, ''
+			|| ''element_id,element_type) ''
+			|| '' VALUES (''||ret.id
+			||'',''||ret.layer_id
+			|| '','' || obj_id || '','' || obj_type || '');'';
+
+		i = i+1;
+		IF i > array_upper(tg_objs, 1) THEN
+			EXIT;
+		END IF;
+	END LOOP;
+
+	RETURN ret;
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} CreateTopoGeom(toponame,topogeom_type, TopoObject[])
+
+--{
+-- GetTopologyName(topology_id)
+--
+CREATEFUNCTION topology.GetTopologyName(integer)
+	RETURNS varchar
+AS
+'
+DECLARE
+	topoid alias for $1;
+	ret varchar;
+BEGIN
+        SELECT name FROM topology.topology into ret
+                WHERE id = topoid;
+	RETURN ret;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} GetTopologyName(topoid)
+
+--{
+-- GetTopologyId(toponame)
+--
+CREATEFUNCTION topology.GetTopologyId(varchar)
+	RETURNS integer
+AS
+'
+DECLARE
+	toponame alias for $1;
+	ret integer;
+BEGIN
+        SELECT id FROM topology.topology into ret
+                WHERE name = toponame;
+	RETURN ret;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} GetTopologyId(toponame)
+	
+
+
+--{
+-- GetTopoGeomElementArray(toponame, layer_id, topogeom_id)
+-- GetTopoGeomElementArray(TopoGeometry)
+--
+-- Returns a set of element_id,element_type
+--
+CREATEFUNCTION topology.GetTopoGeomElementArray(varchar, integer, integer)
+	RETURNS topology.TopoElementArray
+AS
+'
+DECLARE
+	toponame alias for $1;
+	layerid alias for $2;
+	tgid alias for $3;
+	rec RECORD;
+	tg_objs varchar := ''{'';
+	i integer;
+	query text;
+BEGIN
+
+	query = ''SELECT * FROM topology.GetTopoGeomElements(''
+		|| quote_literal(toponame) || '',''
+		|| quote_literal(layerid) || '',''
+		|| quote_literal(tgid)
+		|| '') as obj ORDER BY obj'';
+
+	RAISE NOTICE ''Query: %'', query;
+
+	i = 1;
+	FOR rec IN EXECUTE query
+	LOOP
+		IF i > 1 THEN
+			tg_objs = tg_objs || '','';
+		END IF;
+		tg_objs = tg_objs || ''{''
+			|| rec.obj[1] || '','' || rec.obj[2]
+			|| ''}'';
+		i = i+1;
+	END LOOP;
+
+	tg_objs = tg_objs || ''}'';
+
+	RETURN tg_objs;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.GetTopoGeomElementArray(topology.TopoGeometry)
+	RETURNS topology.TopoElementArray
+AS
+'
+DECLARE
+	tg alias for $1;
+	toponame varchar;
+	ret topology.TopoElementArray;
+BEGIN
+	toponame = topology.GetTopologyName(tg.topology_id);
+	ret = topology.GetTopoGeomElementArray(toponame,tg.layer_id,tg.id);
+	RETURN ret;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+--} GetTopoGeomElementArray()
+
+--{
+-- GetTopoGeomElements(toponame, layer_id, topogeom_id)
+-- GetTopoGeomElements(TopoGeometry)
+--
+-- Returns a set of element_id,element_type
+--
+CREATEFUNCTION topology.GetTopoGeomElements(varchar, integer, integer)
+	RETURNS SETOF topology.TopoElement
+AS
+'
+DECLARE
+	toponame alias for $1;
+	layerid alias for $2;
+	tgid alias for $3;
+	ret topology.TopoElement;
+	rec RECORD;
+	rec2 RECORD;
+	query text;
+	query2 text;
+	lyr RECORD;
+	ok bool;
+BEGIN
+
+	-- Get layer info
+	ok = false;
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| '' topology.layer ''
+		|| '' WHERE layer_id = '' || layerid
+	LOOP
+		lyr = rec;
+		ok = true;
+	END LOOP;
+
+	IF NOT ok THEN
+		RAISE EXCEPTION ''Layer % does not exist'', layerid;
+	END IF;
+
+
+	query = ''SELECT abs(element_id) as element_id, element_type FROM ''
+		|| quote_ident(toponame) || ''.relation WHERE ''
+		|| '' layer_id = '' || layerid
+		|| '' AND topogeo_id = '' || quote_literal(tgid)
+		|| '' ORDER BY element_type, element_id'';
+
+	--RAISE NOTICE ''Query: %'', query;
+
+	FOR rec IN EXECUTE query
+	LOOP
+		IF lyr.level > 0 THEN
+			query2 = ''SELECT * from topology.GetTopoGeomElements(''
+				|| quote_literal(toponame) || '',''
+				|| rec.element_type
+				|| '',''
+				|| rec.element_id
+				|| '') as ret;'';
+			--RAISE NOTICE ''Query2: %'', query2;
+			FOR rec2 IN EXECUTE query2
+			LOOP
+				RETURN NEXT rec2.ret;
+			END LOOP;
+		ELSE
+			ret = ''{'' || rec.element_id || '','' || rec.element_type || ''}'';
+			RETURN NEXT ret;
+		END IF;
+	
+	END LOOP;
+
+	RETURN;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.GetTopoGeomElements(topology.TopoGeometry)
+	RETURNS SETOF topology.TopoElement
+AS
+'
+DECLARE
+	tg alias for $1;
+	toponame varchar;
+	rec RECORD;
+BEGIN
+	toponame = topology.GetTopologyName(tg.topology_id);
+	FOR rec IN SELECT * FROM topology.GetTopoGeomElements(toponame,
+		tg.layer_id,tg.id) as ret
+	LOOP
+		RETURN NEXT rec.ret;
+	END LOOP;
+	RETURN;
+END;
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+--} GetTopoGeomElements()
+
+--{
+-- Geometry(TopoGeometry)
+--
+-- Construct a Geometry from a TopoGeometry.
+-- 
+--
+CREATE OR REPLACE FUNCTION topology.Geometry(topology.TopoGeometry)
+	RETURNS Geometry
+AS '
+DECLARE
+	topogeom alias for $1;
+	toponame varchar;
+	geom geometry;
+	rec RECORD;
+	plyr RECORD;
+	clyr RECORD;
+	query text;
+	ok BOOL;
+BEGIN
+        -- Get topology name
+        SELECT name FROM topology.topology into toponame
+                WHERE id = topogeom.topology_id;
+
+	-- Get layer info
+	ok = false;
+	FOR rec IN EXECUTE ''SELECT * FROM topology.layer ''
+		|| '' WHERE topology_id = '' || topogeom.topology_id
+		|| '' AND layer_id = '' || topogeom.layer_id
+	LOOP
+		ok = true;
+		plyr = rec;
+	END LOOP;
+
+	IF NOT ok THEN
+		RAISE EXCEPTION ''Could not find TopoGeometry layer % in topology %'', topogeom.layer_id, topogeom.topology_id;
+	END IF;
+
+	--
+	-- If this feature layer is on any level > 0 we will
+	-- compute the topological union of all child features
+	-- in fact recursing.
+	--
+	IF plyr.level > 0 THEN
+		-- Get child layer info
+		FOR rec IN EXECUTE ''SELECT * FROM topology.layer''
+			|| '' WHERE layer_id = '' || plyr.child_id
+		LOOP
+			clyr = rec;
+		END LOOP;
+
+		query = ''SELECT geomunion(topology.Geometry(''
+			|| quote_ident(clyr.feature_column)
+			|| '')) as geom FROM ''
+			|| quote_ident(clyr.schema_name) || ''.''
+			|| quote_ident(clyr.table_name)
+			|| '', '' || quote_ident(toponame) || ''.relation pr''
+			|| '' WHERE ''
+			|| '' pr.topogeo_id = '' || topogeom.id
+			|| '' AND ''
+			|| '' pr.layer_id = '' || topogeom.layer_id
+			|| '' AND ''
+			|| '' id(''||quote_ident(clyr.feature_column)
+			|| '') = pr.element_id ''
+			|| '' AND ''
+			|| ''layer_id(''||quote_ident(clyr.feature_column)
+			|| '') = pr.element_type '';
+		--RAISE NOTICE ''%'', query;
+		FOR rec IN EXECUTE query
+		LOOP
+			RETURN rec.geom;
+		END LOOP;
+			
+	END IF;
+	
+
+	IF topogeom.type = 3 THEN -- [multi]polygon
+		FOR rec IN EXECUTE ''SELECT geomunion(''
+			|| ''topology.ST_GetFaceGeometry(''
+			|| quote_literal(toponame) || '',''
+			|| ''element_id)) as g FROM '' 
+			|| quote_ident(toponame)
+			|| ''.relation WHERE topogeo_id = ''
+			|| topogeom.id || '' AND layer_id = ''
+			|| topogeom.layer_id || '' AND element_type = 3 ''
+		LOOP
+			geom := rec.g;
+		END LOOP;
+
+	ELSIF topogeom.type = 2 THEN -- [multi]line
+		FOR rec IN EXECUTE ''SELECT linemerge(collect(e.geom)) as g FROM ''
+			|| quote_ident(toponame) || ''.edge e, ''
+			|| quote_ident(toponame) || ''.relation r ''
+			|| '' WHERE r.topogeo_id = '' || topogeom.id
+			|| '' AND r.layer_id = '' || topogeom.layer_id
+			|| '' AND r.element_type = 2 ''
+			|| '' AND abs(r.element_id) = e.edge_id''
+		LOOP
+			geom := rec.g;
+		END LOOP;
+	
+	ELSIF topogeom.type = 1 THEN -- [multi]point
+		FOR rec IN EXECUTE ''SELECT geomunion(n.geom) as g FROM ''
+			|| quote_ident(toponame) || ''.node n, ''
+			|| quote_ident(toponame) || ''.relation r ''
+			|| '' WHERE r.topogeo_id = '' || topogeom.id
+			|| '' AND r.layer_id = '' || topogeom.layer_id
+			|| '' AND r.element_type = 1 ''
+			|| '' AND r.element_id = n.node_id''
+		LOOP
+			geom := rec.g;
+		END LOOP;
+
+	ELSE
+		RAISE NOTICE ''Geometry from TopoGeometry does not support TopoGeometries of type % so far'', topogeom.type;
+		geom := ''GEOMETRYCOLLECTION EMPTY'';
+	END IF;
+
+	RETURN geom;
+END
+'
+LANGUAGE 'plpgsql' VOLATILE STRICT;
+--} Geometry(TopoGeometry)
+
+--{
+--  ValidateTopology(toponame)
+--
+--  Return a Set of ValidateTopology_ReturnType containing
+--  informations on all topology inconsistencies
+--
+CREATEFUNCTION topology.ValidateTopology(varchar)
+	RETURNS setof topology.ValidateTopology_ReturnType
+AS
+'
+DECLARE
+	toponame alias for $1;
+	retrec topology.ValidateTopology_ReturnType;
+	rec RECORD;
+	i integer;
+BEGIN
+
+	-- Check for coincident nodes
+	FOR rec IN EXECUTE ''SELECT a.node_id as id1, b.node_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.node a, ''
+		|| quote_ident(toponame) || ''.node b ''
+		|| ''WHERE a.node_id < b.node_id AND a.geom && b.geom''
+	LOOP
+		retrec.error = ''coincident nodes'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for edge crossed nodes
+	FOR rec IN EXECUTE ''SELECT n.node_id as id1, e.edge_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.node n, ''
+		|| quote_ident(toponame) || ''.edge e ''
+		|| ''WHERE e.start_node != n.node_id ''
+		|| ''AND e.end_node != n.node_id ''
+		|| ''AND n.geom && e.geom ''
+		|| ''AND intersects(n.geom, e.geom)''
+	LOOP
+		retrec.error = ''edge crosses node'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for non-simple edges
+	FOR rec IN EXECUTE ''SELECT e.edge_id as id1 FROM ''
+		|| quote_ident(toponame) || ''.edge e ''
+		|| ''WHERE not issimple(e.geom)''
+	LOOP
+		retrec.error = ''edge not simple'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = NULL;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for edge crossing
+	FOR rec IN EXECUTE ''SELECT e1.edge_id as id1, e2.edge_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.edge e1, ''
+		|| quote_ident(toponame) || ''.edge e2 ''
+		|| ''WHERE e1.edge_id < e2.edge_id ''
+		|| ''AND e1.geom && e2.geom ''
+		|| ''AND crosses(e1.geom, e2.geom)''
+	LOOP
+		retrec.error = ''edge crosses edge'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for edge start_node geometry mis-match
+	FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.edge e, ''
+		|| quote_ident(toponame) || ''.node n ''
+		|| ''WHERE e.start_node = n.node_id ''
+		|| ''AND NOT Equals(StartPoint(e.geom), n.geom)''
+	LOOP
+		retrec.error = ''edge start node geometry mis-match'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for edge end_node geometry mis-match
+	FOR rec IN EXECUTE ''SELECT e.edge_id as id1, n.node_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.edge e, ''
+		|| quote_ident(toponame) || ''.node n ''
+		|| ''WHERE e.end_node = n.node_id ''
+		|| ''AND NOT Equals(EndPoint(e.geom), n.geom)''
+	LOOP
+		retrec.error = ''edge end node geometry mis-match'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for faces w/out edges
+	FOR rec IN EXECUTE ''SELECT face_id as id1 FROM ''
+		|| quote_ident(toponame) || ''.face ''
+		|| ''EXCEPT ( SELECT left_face FROM ''
+		|| quote_ident(toponame) || ''.edge ''
+		|| '' UNION SELECT right_face FROM ''
+		|| quote_ident(toponame) || ''.edge ''
+		|| '')''
+	LOOP
+		retrec.error = ''face without edges'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = NULL;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for overlapping faces 
+	-- TODO: this check requires some thinking, do we really
+	--       have to construct face geometry to detect overlap with
+	--       another face ??
+	FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.face f1, ''
+		|| quote_ident(toponame) || ''.face f2 ''
+		|| ''WHERE f1.face_id > 0 AND f1.face_id < f2.face_id AND ''
+		|| '' Overlaps(topology.ST_GetFaceGeometry(''
+		|| quote_literal(toponame) || '', f1.face_id), ''
+		|| '' topology.ST_GetFaceGeometry(''
+		|| quote_literal(toponame) || '', f2.face_id))''
+	LOOP
+		retrec.error = ''face overlaps face'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+	-- Check for face within face
+	-- TODO: this check requires some thinking, do we really
+	--       have to construct face geometry to detect within condition
+	--       another face ??
+	FOR rec IN EXECUTE ''SELECT f1.face_id as id1, f2.face_id as id2 FROM ''
+		|| quote_ident(toponame) || ''.face f1, ''
+		|| quote_ident(toponame) || ''.face f2 ''
+		|| ''WHERE f1.face_id != 0 AND f2.face_id != 0 ''
+		|| ''AND f1.face_id != f2.face_id ''
+		|| ''AND Within(topology.ST_GetFaceGeometry(''
+		|| quote_literal(toponame) || '', f1.face_id), ''
+		|| '' topology.ST_GetFaceGeometry(''
+		|| quote_literal(toponame) || '', f2.face_id))''
+	LOOP
+		retrec.error = ''face within face'';
+		retrec.id1 = rec.id1;
+		retrec.id2 = rec.id2;
+		RETURN NEXT retrec;
+	END LOOP;
+
+#if 0
+	-- Check SRID consistency
+	FOR rec in EXECUTE
+		''SELECT count(*) FROM ( getSRID(geom) FROM ''
+		|| quote_ident(toponame) || ''.edge ''
+		|| '' UNION ''
+		''SELECT getSRID(geom) FROM ''
+		|| quote_ident(toponame) || ''.node )''
+	LOOP
+		IF rec.count > 1 THEN
+			retrec.error = ''mixed SRIDs'';
+			retrec.id1 = NULL;
+			retrec.id2 = NULL;
+			RETURN NEXT retrec;
+		END IF;
+	END LOOP;
+#endif
+
+	RETURN;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+-- } ValidateTopology(toponame)
+
+--{
+--  TopoGeo_AddPoint(toponame, pointgeom, layer_id, topogeom_id)
+--
+--  Add a Point (node) into a topology 
+--
+CREATEFUNCTION topology.TopoGeo_AddPoint(varchar, geometry, integer, integer)
+	RETURNS int AS
+'
+DECLARE
+	atopology alias for $1;
+	apoint alias for $2;
+	ret int;
+BEGIN
+
+	-- Add nodes (contained in NO face)
+	SELECT topology.ST_AddIsoNode(atopology, NULL, apoint) INTO ret;
+
+	RAISE NOTICE ''TopoGeo_AddPoint: node: %'', ret;
+
+	RETURN ret;
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} TopoGeo_AddPoint
+
+--{
+--  TopoGeo_addLinestring
+--
+--  Add a Point (node) into a topology 
+--
+CREATEFUNCTION topology.TopoGeo_addLinestring(varchar, geometry)
+	RETURNS int AS
+'
+DECLARE
+	atopology alias for $1;
+	aline alias for $2;
+	rec RECORD;
+	query text;
+	firstpoint geometry;
+	lastpoint geometry;
+	firstnode int;
+	lastnode int;
+	edgeid int;
+BEGIN
+	firstpoint = StartPoint(aline);
+	lastpoint = EndPoint(aline);
+
+	-- Add first and last point nodes (contained in NO face)
+	SELECT topology.ST_AddIsoNode(atopology, NULL, firstpoint) INTO firstnode;
+	SELECT topology.ST_AddIsoNode(atopology, NULL, lastpoint) INTO lastnode;
+
+	RAISE NOTICE ''First node: %, Last node: %'', firstnode, lastnode;
+
+	-- Add edge 
+	SELECT topology.ST_AddIsoEdge(atopology, firstnode, lastnode, aline)
+		INTO edgeid;
+
+	RAISE NOTICE ''Edge: %'', edgeid;
+
+	RETURN edgeid;
+END
+'
+LANGUAGE 'plpgsql';
+--} TopoGeo_addLinestring
+
+--{
+--  TopoGeo_AddPolygon
+--
+--  Add a Polygon into a topology 
+--
+CREATEFUNCTION topology.TopoGeo_AddPolygon(varchar, geometry)
+	RETURNS int AS
+'
+DECLARE
+	rec RECORD;
+	query text;
+BEGIN
+
+	RAISE EXCEPTION ''TopoGeo_AddPolygon not implemented yet'';
+END
+'
+LANGUAGE 'plpgsql';
+--} TopoGeo_AddPolygon
+
+--{
+--  CreateTopology(name, SRID, precision)
+--
+-- Create a topology schema, add a topology info record
+-- in the topology.topology relation, return it's numeric
+-- id. 
+--
+CREATEFUNCTION topology.CreateTopology(varchar, integer, float8)
+RETURNS integer
+AS '
+DECLARE
+	atopology alias for $1;
+	srid alias for $2;
+	precision alias for $3;
+	rec RECORD;
+	topology_id integer;
+BEGIN
+
+--	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+--	LOOP
+--		RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
+--	END LOOP;
+
+	------ Fetch next id for the new topology
+	FOR rec IN SELECT nextval(''topology.topology_id_seq'')
+	LOOP
+		topology_id = rec.nextval;
+	END LOOP;
+
+
+	EXECUTE ''
+CREATE SCHEMA '' || quote_ident(atopology) || '';
+	'';
+
+	-------------{ face CREATION
+	EXECUTE 
+	''CREATE TABLE '' || quote_ident(atopology) || ''.face (''
+	|| ''face_id SERIAL,''
+	|| ''mbr BOX2D,''
+	|| '' CONSTRAINT face_primary_key PRIMARY KEY(face_id)''
+	|| '');'';
+	-------------} END OF face CREATION
+
+
+	--------------{ node CREATION
+
+	EXECUTE 
+	''CREATE TABLE '' || quote_ident(atopology) || ''.node (''
+	|| ''node_id SERIAL,''
+	--|| ''geom GEOMETRY,''
+	|| ''containing_face INTEGER,''
+
+	|| ''CONSTRAINT node_primary_key PRIMARY KEY(node_id),''
+
+	--|| ''CONSTRAINT node_geometry_type CHECK ''
+	--|| ''( GeometryType(geom) = ''''POINT'''' ),''
+
+	|| ''CONSTRAINT face_exists FOREIGN KEY(containing_face) ''
+	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id)''
+
+	|| '');'';
+
+	-- Add geometry column to the node table 
+	EXECUTE
+	''SELECT AddGeometryColumn(''||quote_literal(atopology)
+	||'',''''node'''',''''geom'''',''||quote_literal(srid)
+	||'',''''POINT'''',''''2'''')'';
+
+	--------------} END OF node CREATION
+
+	--------------{ edge CREATION
+
+	-- edge_data table
+	EXECUTE 
+	''CREATE TABLE '' || quote_ident(atopology) || ''.edge_data (''
+	|| ''edge_id SERIAL NOT NULL PRIMARY KEY,''
+	|| ''start_node INTEGER NOT NULL,''
+	|| ''end_node INTEGER NOT NULL,''
+	|| ''next_left_edge INTEGER NOT NULL,''
+	|| ''abs_next_left_edge INTEGER NOT NULL,''
+	|| ''next_right_edge INTEGER NOT NULL,''
+	|| ''abs_next_right_edge INTEGER NOT NULL,''
+	|| ''left_face INTEGER NOT NULL,''
+	|| ''right_face INTEGER NOT NULL,''
+	--|| ''geom GEOMETRY NOT NULL,''
+
+	--|| ''CONSTRAINT edge_geometry_type CHECK ''
+	--|| ''( GeometryType(geom) = ''''LINESTRING'''' ),''
+
+	|| ''CONSTRAINT start_node_exists FOREIGN KEY(start_node)''
+	|| '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
+
+	|| ''CONSTRAINT end_node_exists FOREIGN KEY(end_node) ''
+	|| '' REFERENCES '' || quote_ident(atopology) || ''.node(node_id),''
+
+	|| ''CONSTRAINT left_face_exists FOREIGN KEY(left_face) ''
+	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
+
+	|| ''CONSTRAINT right_face_exists FOREIGN KEY(right_face) ''
+	|| ''REFERENCES '' || quote_ident(atopology) || ''.face(face_id),''
+
+	|| ''CONSTRAINT next_left_edge_exists FOREIGN KEY(abs_next_left_edge)''
+	|| '' REFERENCES '' || quote_ident(atopology)
+	|| ''.edge_data(edge_id)''
+	|| '' DEFERRABLE INITIALLY DEFERRED,''
+
+	|| ''CONSTRAINT next_right_edge_exists ''
+	|| ''FOREIGN KEY(abs_next_right_edge)''
+	|| '' REFERENCES '' || quote_ident(atopology)
+	|| ''.edge_data(edge_id) ''
+	|| '' DEFERRABLE INITIALLY DEFERRED''
+	|| '');'';
+
+	-- Add geometry column to the edge_data table 
+	EXECUTE
+	''SELECT AddGeometryColumn(''||quote_literal(atopology)
+	||'',''''edge_data'''',''''geom'''',''||quote_literal(srid)
+	||'',''''LINESTRING'''',''''2'''')'';
+
+
+	-- edge standard view (select rule)
+	EXECUTE ''CREATE VIEW '' || quote_ident(atopology)
+		|| ''.edge AS SELECT ''
+		|| '' edge_id,start_node, end_node, next_left_edge, ''
+		|| '' next_right_edge, ''
+		|| '' left_face, right_face, geom FROM ''
+		|| quote_ident(atopology) || ''.edge_data'';
+
+	-- edge standard view (insert rule)
+	EXECUTE ''CREATE RULE edge_insert_rule AS ON INSERT ''
+        	|| ''TO '' || quote_ident(atopology)
+		|| ''.edge DO INSTEAD ''
+                || '' INSERT into '' || quote_ident(atopology)
+		|| ''.edge_data ''
+                || '' VALUES (NEW.edge_id, NEW.start_node, NEW.end_node, ''
+		|| '' NEW.next_left_edge, abs(NEW.next_left_edge), ''
+		|| '' NEW.next_right_edge, abs(NEW.next_right_edge), ''
+		|| '' NEW.left_face, NEW.right_face, NEW.geom);'';
+
+	--------------} END OF edge CREATION
+
+	--------------{ layer sequence 
+	EXECUTE ''CREATE SEQUENCE ''
+		|| quote_ident(atopology) || ''.layer_id_seq;'';
+	--------------} layer sequence
+
+	--------------{ relation CREATION
+	--
+	EXECUTE 
+	''CREATE TABLE '' || quote_ident(atopology) || ''.relation (''
+	|| '' topogeo_id integer NOT NULL, ''
+	|| '' layer_id integer NOT NULL, '' 
+	|| '' element_id integer NOT NULL, ''
+	|| '' element_type integer NOT NULL, ''
+	|| '' UNIQUE(layer_id,topogeo_id,element_id,element_type));'';
+
+	EXECUTE 
+	''CREATE TRIGGER relation_integrity_checks ''
+	||''BEFORE UPDATE OR INSERT ON ''
+	|| quote_ident(atopology) || ''.relation FOR EACH ROW ''
+	|| '' EXECUTE PROCEDURE topology.RelationTrigger(''
+	||topology_id||'',''||quote_literal(atopology)||'')'';
+	--------------} END OF relation CREATION
+
+	
+	------- Default (world) face
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology) || ''.face(face_id) VALUES(0);'';
+
+	------- GiST index on node
+	EXECUTE ''CREATE INDEX node_gist ON ''
+		|| quote_ident(atopology)
+		|| ''.node using gist (geom gist_geometry_ops);'';
+
+	------- GiST index on edge
+	EXECUTE ''CREATE INDEX edge_gist ON ''
+		|| quote_ident(atopology)
+		|| ''.edge_data using gist (geom gist_geometry_ops);'';
+
+	------- Add record to the "topology" metadata table
+	EXECUTE ''INSERT INTO topology.topology (id, name, srid, precision) ''
+		|| '' VALUES ('' || quote_literal(topology_id) || '',''
+		|| quote_literal(atopology) || '',''
+		|| quote_literal(srid) || '','' || quote_literal(precision)
+		|| '')'';
+
+	RETURN topology_id;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+
+-- wrappers for unspecified srid or precision
+
+CREATEFUNCTION topology.CreateTopology(varchar, integer)
+RETURNS integer AS
+' SELECT topology.CreateTopology($1, $2, -1); '
+LANGUAGE 'SQL' _VOLATILE_STRICT;
+
+CREATEFUNCTION topology.CreateTopology(varchar)
+RETURNS integer AS
+' SELECT topology.CreateTopology($1, -1, -1); '
+LANGUAGE 'SQL' _VOLATILE_STRICT;
+
+--} CreateTopology
+
+--{
+--  DropTopology(name)
+--
+-- Drops a topology schema getting rid of every dependent object.
+--
+CREATEFUNCTION topology.DropTopology(varchar)
+RETURNS text
+AS '
+DECLARE
+	atopology alias for $1;
+	topoid integer;
+	rec RECORD;
+BEGIN
+
+	-- Get topology id
+        SELECT id FROM topology.topology into topoid
+                WHERE name = atopology;
+
+
+	IF topoid IS NOT NULL THEN
+
+		RAISE NOTICE ''Dropping all layers from topology % (%)'',
+			atopology, topoid;
+
+		-- Drop all layers in the topology
+		FOR rec IN EXECUTE ''SELECT * FROM topology.layer WHERE ''
+			|| '' topology_id = '' || topoid
+		LOOP
+
+			EXECUTE ''SELECT topology.DropTopoGeometryColumn(''
+				|| quote_literal(rec.schema_name)
+				|| '',''
+				|| quote_literal(rec.table_name)
+				|| '',''
+				|| quote_literal(rec.feature_column)
+				|| '')'';
+		END LOOP;
+
+		-- Delete record from topology.topology
+		EXECUTE ''DELETE FROM topology.topology WHERE id = ''
+			|| topoid;
+
+	END IF;
+
+
+	-- Drop the schema (if it exists)
+	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+	LOOP
+		EXECUTE ''DROP SCHEMA ''||quote_ident(atopology)||'' CASCADE'';
+	END LOOP;
+
+
+	RETURN ''Topology '' || quote_literal(atopology) || '' dropped'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} DropTopology
+
+--={ ----------------------------------------------------------------
+--  POSTGIS-SPECIFIC topology predicates
+--
+--  This part contains function NOT in the SQL/MM specification
+--
+---------------------------------------------------------------------
+
+--{
+-- Intersects(TopoGeometry, TopoGeometry)
+--
+CREATEFUNCTION topology.intersects(topology.TopoGeometry, topology.TopoGeometry)
+	RETURNS bool
+AS
+'
+DECLARE
+	tg1 alias for $1;
+	tg2 alias for $2;
+	tgbuf topology.TopoGeometry;
+	rec RECORD;
+	toponame varchar;
+	query text;
+BEGIN
+	IF tg1.topology_id != tg2.topology_id THEN
+		RAISE EXCEPTION ''Cannot compute intersection between TopoGeometries from different topologies'';
+	END IF;
+
+	-- Order TopoGeometries so that tg1 has less-or-same
+	-- dimensionality of tg1 (point,line,polygon,collection)
+	IF tg1.type > tg2.type THEN
+		tgbuf := tg2;
+		tg2 := tg1;
+		tg1 := tgbuf;
+	END IF;
+
+	--RAISE NOTICE ''tg1.id:% tg2.id:%'', tg1.id, tg2.id;
+	-- Geometry collection are not currently supported
+	IF tg2.type = 4 THEN
+		RAISE EXCEPTION ''GeometryCollection are not supported by intersects()'';
+	END IF;
+
+        -- Get topology name
+        SELECT name FROM topology.topology into toponame
+                WHERE id = tg1.topology_id;
+
+	-- Hierarchical TopoGeometries are not currently supported
+	query = ''SELECT level FROM topology.layer''
+		|| '' WHERE ''
+		|| '' topology_id = '' || tg1.topology_id
+		|| '' AND ''
+		|| ''( layer_id = '' || tg1.layer_id
+		|| '' OR layer_id = '' || tg2.layer_id
+		|| '' ) ''
+		|| '' AND level > 0 '';
+
+	--RAISE NOTICE ''%'', query;
+
+	FOR rec IN EXECUTE query
+	LOOP
+		RAISE EXCEPTION ''Hierarchical TopoGeometries are not currently supported by intersects()'';
+	END LOOP;
+
+	IF tg1.type = 1 THEN -- [multi]point
+
+
+		IF tg2.type = 1 THEN -- point/point
+	---------------------------------------------------------
+	-- 
+	--  Two [multi]point features intersect if they share
+	--  any Node 
+	--
+	--
+	--
+			query =
+				''SELECT a.topogeo_id FROM ''
+				|| quote_ident(toponame) ||
+				''.relation a, ''
+				|| quote_ident(toponame) ||
+				''.relation b ''
+				|| ''WHERE a.layer_id = '' || tg1.layer_id
+				|| '' AND b.layer_id = '' || tg2.layer_id
+				|| '' AND a.topogeo_id = '' || tg1.id
+				|| '' AND b.topogeo_id = '' || tg2.id
+				|| '' AND a.element_id = b.element_id ''
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				RETURN TRUE; -- they share an element
+			END LOOP;
+			RETURN FALSE; -- no elements shared
+	--
+	---------------------------------------------------------
+			
+
+		ELSIF tg2.type = 2 THEN -- point/line
+	---------------------------------------------------------
+	-- 
+	--  A [multi]point intersects a [multi]line if they share
+	--  any Node. 
+	--
+	--
+	--
+			query =
+				''SELECT a.topogeo_id FROM ''
+				|| quote_ident(toponame) ||
+				''.relation a, ''
+				|| quote_ident(toponame) ||
+				''.relation b, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e ''
+				|| ''WHERE a.layer_id = '' || tg1.layer_id
+				|| '' AND b.layer_id = '' || tg2.layer_id
+				|| '' AND a.topogeo_id = '' || tg1.id
+				|| '' AND b.topogeo_id = '' || tg2.id
+				|| '' AND abs(b.element_id) = e.edge_id ''
+				|| '' AND ( ''
+					|| '' e.start_node = a.element_id ''
+					|| '' OR ''
+					|| '' e.end_node = a.element_id ''
+				|| '' )''
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				RETURN TRUE; -- they share an element
+			END LOOP;
+			RETURN FALSE; -- no elements shared
+	--
+	---------------------------------------------------------
+
+		ELSIF tg2.type = 3 THEN -- point/polygon
+	---------------------------------------------------------
+	-- 
+	--  A [multi]point intersects a [multi]polygon if any
+	--  Node of the point is contained in any face of the
+	--  polygon OR ( is end_node or start_node of any edge
+	--  of any polygon face ).
+	--
+	--  We assume the Node-in-Face check is faster becasue
+	--  there will be less Faces then Edges in any polygon.
+	--
+	--
+	--
+	--
+			-- Check if any node is contained in a face
+			query =
+				''SELECT n.node_id as id FROM ''
+				|| quote_ident(toponame) ||
+				''.relation r1, ''
+				|| quote_ident(toponame) ||
+				''.relation r2, ''
+				|| quote_ident(toponame) ||
+				''.node n ''
+				|| ''WHERE r1.layer_id = '' || tg1.layer_id
+				|| '' AND r2.layer_id = '' || tg2.layer_id
+				|| '' AND r1.topogeo_id = '' || tg1.id
+				|| '' AND r2.topogeo_id = '' || tg2.id
+				|| '' AND n.node_id = r1.element_id ''
+				|| '' AND r2.element_id = n.containing_face ''
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				--RAISE NOTICE ''Node % in polygon face'', rec.id;
+				RETURN TRUE; -- one (or more) nodes are
+				             -- contained in a polygon face
+			END LOOP;
+
+			-- Check if any node is start or end of any polygon
+			-- face edge
+			query =
+				''SELECT n.node_id as nid, e.edge_id as eid ''
+				|| '' FROM ''
+				|| quote_ident(toponame) ||
+				''.relation r1, ''
+				|| quote_ident(toponame) ||
+				''.relation r2, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e, ''
+				|| quote_ident(toponame) ||
+				''.node n ''
+				|| ''WHERE r1.layer_id = '' || tg1.layer_id
+				|| '' AND r2.layer_id = '' || tg2.layer_id
+				|| '' AND r1.topogeo_id = '' || tg1.id
+				|| '' AND r2.topogeo_id = '' || tg2.id
+				|| '' AND n.node_id = r1.element_id ''
+				|| '' AND ( ''
+				|| '' e.left_face = r2.element_id ''
+				|| '' OR ''
+				|| '' e.right_face = r2.element_id ''
+				|| '' ) ''
+				|| '' AND ( ''
+				|| '' e.start_node = r1.element_id ''
+				|| '' OR ''
+				|| '' e.end_node = r1.element_id ''
+				|| '' ) ''
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				--RAISE NOTICE ''Node % on edge % bound'', rec.nid, rec.eid;
+				RETURN TRUE; -- one node is start or end
+				             -- of a face edge
+			END LOOP;
+
+			RETURN FALSE; -- no intersection
+	--
+	---------------------------------------------------------
+
+		ELSIF tg2.type = 4 THEN -- point/collection
+			RAISE EXCEPTION ''Intersection point/collection not implemented yet'';
+
+		ELSE
+			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+		END IF;
+
+	ELSIF tg1.type = 2 THEN -- [multi]line
+		IF tg2.type = 2 THEN -- line/line
+	---------------------------------------------------------
+	-- 
+	--  A [multi]line intersects a [multi]line if they share
+	--  any Node. 
+	--
+	--
+	--
+			query =
+				''SELECT e1.start_node FROM ''
+				|| quote_ident(toponame) ||
+				''.relation r1, ''
+				|| quote_ident(toponame) ||
+				''.relation r2, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e1, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e2 ''
+				|| ''WHERE r1.layer_id = '' || tg1.layer_id
+				|| '' AND r2.layer_id = '' || tg2.layer_id
+				|| '' AND r1.topogeo_id = '' || tg1.id
+				|| '' AND r2.topogeo_id = '' || tg2.id
+				|| '' AND abs(r1.element_id) = e1.edge_id ''
+				|| '' AND abs(r2.element_id) = e2.edge_id ''
+				|| '' AND ( ''
+					|| '' e1.start_node = e2.start_node ''
+					|| '' OR ''
+					|| '' e1.start_node = e2.end_node ''
+					|| '' OR ''
+					|| '' e1.end_node = e2.start_node ''
+					|| '' OR ''
+					|| '' e1.end_node = e2.end_node ''
+				|| '' )''
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				RETURN TRUE; -- they share an element
+			END LOOP;
+			RETURN FALSE; -- no elements shared
+	--
+	---------------------------------------------------------
+
+		ELSIF tg2.type = 3 THEN -- line/polygon
+	---------------------------------------------------------
+	-- 
+	-- A [multi]line intersects a [multi]polygon if they share
+	-- any Node (touch-only case), or if any line edge has any
+	-- polygon face on the left or right (full-containment case
+	-- + edge crossing case).
+	--
+	--
+			-- E1 are line edges, E2 are polygon edges
+			-- R1 are line relations.
+			-- R2 are polygon relations.
+			-- R2.element_id are FACE ids
+			query =
+				''SELECT e1.edge_id''
+				|| '' FROM ''
+				|| quote_ident(toponame) ||
+				''.relation r1, ''
+				|| quote_ident(toponame) ||
+				''.relation r2, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e1, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e2 ''
+				|| ''WHERE r1.layer_id = '' || tg1.layer_id
+				|| '' AND r2.layer_id = '' || tg2.layer_id
+				|| '' AND r1.topogeo_id = '' || tg1.id
+				|| '' AND r2.topogeo_id = '' || tg2.id
+
+				-- E1 are line edges
+				|| '' AND e1.edge_id = abs(r1.element_id) ''
+
+				-- E2 are face edges
+				|| '' AND ( e2.left_face = r2.element_id ''
+				|| ''   OR e2.right_face = r2.element_id ) ''
+
+				|| '' AND ( ''
+
+				-- Check if E1 have left-or-right face 
+				-- being part of R2.element_id
+				|| '' e1.left_face = r2.element_id ''
+				|| '' OR ''
+				|| '' e1.right_face = r2.element_id ''
+
+				-- Check if E1 share start-or-end node
+				-- with any E2.
+				|| '' OR ''
+				|| '' e1.start_node = e2.start_node ''
+				|| '' OR ''
+				|| '' e1.start_node = e2.end_node ''
+				|| '' OR ''
+				|| '' e1.end_node = e2.start_node ''
+				|| '' OR ''
+				|| '' e1.end_node = e2.end_node ''
+
+				|| '' ) ''
+
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				RETURN TRUE; -- either common node
+				             -- or edge-in-face
+			END LOOP;
+
+			RETURN FALSE; -- no intersection
+	--
+	---------------------------------------------------------
+
+		ELSIF tg2.type = 4 THEN -- line/collection
+			RAISE EXCEPTION ''Intersection line/collection not implemented yet'', tg1.type, tg2.type;
+
+		ELSE
+			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+		END IF;
+
+
+	ELSIF tg1.type = 3 THEN -- [multi]polygon
+
+		IF tg2.type = 3 THEN -- polygon/polygon
+	---------------------------------------------------------
+	-- 
+	-- A [multi]polygon intersects a [multi]polygon if they share
+	-- any Node (touch-only case), or if any face edge has any of the
+	-- other polygon face on the left or right (full-containment case
+	-- + edge crossing case).
+	--
+	--
+			-- E1 are poly1 edges.
+			-- E2 are poly2 edges
+			-- R1 are poly1 relations.
+			-- R2 are poly2 relations.
+			-- R1.element_id are poly1 FACE ids
+			-- R2.element_id are poly2 FACE ids
+			query =
+				''SELECT e1.edge_id''
+				|| '' FROM ''
+				|| quote_ident(toponame) ||
+				''.relation r1, ''
+				|| quote_ident(toponame) ||
+				''.relation r2, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e1, ''
+				|| quote_ident(toponame) ||
+				''.edge_data e2 ''
+				|| ''WHERE r1.layer_id = '' || tg1.layer_id
+				|| '' AND r2.layer_id = '' || tg2.layer_id
+				|| '' AND r1.topogeo_id = '' || tg1.id
+				|| '' AND r2.topogeo_id = '' || tg2.id
+
+				-- E1 are poly1 edges
+				|| '' AND ( e1.left_face = r1.element_id ''
+				|| ''   OR e1.right_face = r1.element_id ) ''
+
+				-- E2 are poly2 edges
+				|| '' AND ( e2.left_face = r2.element_id ''
+				|| ''   OR e2.right_face = r2.element_id ) ''
+
+				|| '' AND ( ''
+
+				-- Check if any edge from a polygon face
+				-- has any of the other polygon face
+				-- on the left or right 
+				|| '' e1.left_face = r2.element_id ''
+				|| '' OR ''
+				|| '' e1.right_face = r2.element_id ''
+				|| '' OR ''
+				|| '' e2.left_face = r1.element_id ''
+				|| '' OR ''
+				|| '' e2.right_face = r1.element_id ''
+
+				-- Check if E1 share start-or-end node
+				-- with any E2.
+				|| '' OR ''
+				|| '' e1.start_node = e2.start_node ''
+				|| '' OR ''
+				|| '' e1.start_node = e2.end_node ''
+				|| '' OR ''
+				|| '' e1.end_node = e2.start_node ''
+				|| '' OR ''
+				|| '' e1.end_node = e2.end_node ''
+
+				|| '' ) ''
+
+				|| '' LIMIT 1'';
+			--RAISE NOTICE ''%'', query;
+			FOR rec IN EXECUTE query
+			LOOP
+				RETURN TRUE; -- either common node
+				             -- or edge-in-face
+			END LOOP;
+
+			RETURN FALSE; -- no intersection
+	--
+	---------------------------------------------------------
+
+		ELSIF tg2.type = 4 THEN -- polygon/collection
+			RAISE EXCEPTION ''Intersection poly/collection not implemented yet'', tg1.type, tg2.type;
+
+		ELSE
+			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+		END IF;
+
+	ELSIF tg1.type = 4 THEN -- collection
+		IF tg2.type = 4 THEN -- collection/collection
+			RAISE EXCEPTION ''Intersection collection/collection not implemented yet'', tg1.type, tg2.type;
+		ELSE
+			RAISE EXCEPTION ''Invalid TopoGeometry type'', tg2.type;
+		END IF;
+
+	ELSE
+		RAISE EXCEPTION ''Invalid TopoGeometry type %'', tg1.type;
+	END IF;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} intersects(TopoGeometry, TopoGeometry)
+
+--{
+--  equals(TopoGeometry, TopoGeometry)
+--
+CREATEFUNCTION topology.equals(topology.TopoGeometry, topology.TopoGeometry)
+	RETURNS bool
+AS
+'
+DECLARE
+	tg1 alias for $1;
+	tg2 alias for $2;
+	rec RECORD;
+	toponame varchar;
+	query text;
+BEGIN
+
+	IF tg1.topology_id != tg2.topology_id THEN
+		RAISE EXCEPTION ''Cannot compare TopoGeometries from different topologies'';
+	END IF;
+
+	-- Not the same type, not equal
+	IF tg1.type != tg2.type THEN
+		RETURN FALSE;
+	END IF;
+
+	-- Geometry collection are not currently supported
+	IF tg2.type = 4 THEN
+		RAISE EXCEPTION ''GeometryCollection are not supported by equals()'';
+	END IF;
+
+        -- Get topology name
+        SELECT name FROM topology.topology into toponame
+                WHERE id = tg1.topology_id;
+
+	-- Two geometries are equal if they are composed by 
+	-- the same TopoElements
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| '' topology.GetTopoGeomElements(''
+		|| quote_literal(toponame) || '', ''
+		|| tg1.layer_id || '','' || tg1.id || '') ''
+		|| '' EXCEPT SELECT * FROM ''
+		|| '' topology.GetTopogeomElements(''
+		|| quote_literal(toponame) || '', ''
+		|| tg2.layer_id || '','' || tg2.id || '');''
+	LOOP
+		RETURN FALSE;
+	END LOOP;
+
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| '' topology.GetTopoGeomElements(''
+		|| quote_literal(toponame) || '', ''
+		|| tg2.layer_id || '','' || tg2.id || '')''
+		|| '' EXCEPT SELECT * FROM ''
+		|| '' topology.GetTopogeomElements(''
+		|| quote_literal(toponame) || '', ''
+		|| tg1.layer_id || '','' || tg1.id || ''); ''
+	LOOP
+		RETURN FALSE;
+	END LOOP;
+	RETURN TRUE;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE_STRICT;
+--} equals(TopoGeometry, TopoGeometry)
+
+--=} POSTGIS-SPECIFIC topology predicates
+
+
+--=}  POSTGIS-SPECIFIC block
+
+--={ ----------------------------------------------------------------
+--  SQL/MM block
+--
+--  This part contains function in the SQL/MM specification
+--
+---------------------------------------------------------------------
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.5
+--
+--  ST_GetFaceEdges(atopology, aface)
+--
+--
+-- 
+CREATEFUNCTION topology.ST_GetFaceEdges(varchar, integer)
+	RETURNS setof topology.GetFaceEdges_ReturnType
+AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	aface ALIAS FOR $2;
+	rec RECORD;
+BEGIN
+	--
+	-- Atopology and aface are required
+	-- 
+	IF atopology IS NULL OR aface IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	RAISE EXCEPTION
+		 ''ST_GetFaceEdges: not implemented yet'';
+
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_GetFaceEdges
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.16
+--
+--  ST_GetFaceGeometry(atopology, aface)
+-- 
+CREATEFUNCTION topology.ST_GetFaceGeometry(varchar, integer)
+	RETURNS GEOMETRY AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	aface ALIAS FOR $2;
+	rec RECORD;
+BEGIN
+	--
+	-- Atopology and aface are required
+	-- 
+	IF atopology IS NULL OR aface IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Construct face 
+	-- 
+	FOR rec IN EXECUTE ''SELECT polygonize(geom) FROM ( SELECT geom FROM ''
+		|| quote_ident(atopology)
+		|| ''.edge WHERE left_face = '' || aface || 
+		'' OR right_face = '' || aface || '') as foo''
+	LOOP
+		RETURN geometryN(rec.polygonize, 1);
+		--RETURN rec.polygonize;
+	END LOOP;
+
+
+	--
+	-- No face found
+	-- 
+	RAISE EXCEPTION
+	''SQL/MM Spatial exception - non-existent face.'';
+END
+' 
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_GetFaceGeometry
+
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.1 
+--
+--  ST_AddIsoNode(atopology, aface, apoint)
+--
+CREATEFUNCTION topology.ST_AddIsoNode(varchar, integer, geometry)
+	RETURNS INTEGER AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	aface ALIAS FOR $2;
+	apoint ALIAS FOR $3;
+	rec RECORD;
+	nodeid integer;
+BEGIN
+
+	--
+	-- Atopology and apoint are required
+	-- 
+	IF atopology IS NULL OR apoint IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Apoint must be a point
+	--
+	IF substring(geometrytype(apoint), 1, 5) != ''POINT''
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - invalid point'';
+	END IF;
+
+	--
+	-- Check if a coincident node already exists
+	-- 
+	-- We use index AND x/y equality
+	--
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node '' ||
+		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - coincident node'';
+	END LOOP;
+
+	--
+	-- Check if any edge crosses (intersects) this node
+	-- I used _intersects_ here to include boundaries (endpoints)
+	--
+	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+		|| quote_ident(atopology) || ''.edge '' 
+		|| ''WHERE geom && '' || quote_literal(apoint) 
+		|| '' AND intersects(geom, '' || quote_literal(apoint)
+		|| '')''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - edge crosses node.'';
+	END LOOP;
+
+
+	--
+	-- Verify that aface contains apoint
+	-- if aface is null no check is done
+	--
+	IF aface IS NOT NULL THEN
+
+	FOR rec IN EXECUTE ''SELECT within(''
+		|| quote_literal(apoint) || ''::geometry, 
+		topology.ST_GetFaceGeometry(''
+		|| quote_literal(atopology) || '', '' || aface ||
+		''))''
+	LOOP
+		IF rec.within = ''f'' THEN
+			RAISE EXCEPTION
+			''SQL/MM Spatial exception - not within face'';
+		ELSIF rec.within IS NULL THEN
+			RAISE EXCEPTION
+			''SQL/MM Spatial exception - non-existent face'';
+		END IF;
+	END LOOP;
+
+	END IF;
+
+	--
+	-- Get new node id from sequence
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.node_node_id_seq'''')''
+	LOOP
+		nodeid = rec.nextval;
+	END LOOP;
+
+	--
+	-- Insert the new row
+	--
+	IF aface IS NOT NULL THEN
+		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+			|| ''.node(node_id, geom, containing_face) 
+			VALUES(''||nodeid||'',''||quote_literal(apoint)||
+			'',''||aface||'')'';
+	ELSE
+		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+			|| ''.node(node_id, geom) 
+			VALUES(''||nodeid||'',''||quote_literal(apoint)||
+			'')'';
+	END IF;
+
+	RETURN nodeid;
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.2 
+--
+--  ST_MoveIsoNode(atopology, anode, apoint)
+--
+CREATEFUNCTION topology.ST_MoveIsoNode(varchar, integer, geometry)
+	RETURNS TEXT AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anode ALIAS FOR $2;
+	apoint ALIAS FOR $3;
+	rec RECORD;
+BEGIN
+
+	--
+	-- All arguments are required
+	-- 
+	IF atopology IS NULL OR anode IS NULL OR apoint IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Apoint must be a point
+	--
+	IF substring(geometrytype(apoint), 1, 5) != ''POINT''
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - invalid point'';
+	END IF;
+
+	--
+	-- Check node isolation.
+	-- 
+	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+		|| quote_ident(atopology) || ''.edge '' ||
+		'' WHERE start_node =  '' || anode ||
+		'' OR end_node = '' || anode 
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - not isolated node'';
+	END LOOP;
+
+	--
+	-- Check if a coincident node already exists
+	-- 
+	-- We use index AND x/y equality
+	--
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node '' ||
+		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - coincident node'';
+	END LOOP;
+
+	--
+	-- Check if any edge crosses (intersects) this node
+	-- I used _intersects_ here to include boundaries (endpoints)
+	--
+	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+		|| quote_ident(atopology) || ''.edge '' 
+		|| ''WHERE geom && '' || quote_literal(apoint) 
+		|| '' AND intersects(geom, '' || quote_literal(apoint)
+		|| '')''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - edge crosses node.'';
+	END LOOP;
+
+	--
+	-- Update node point
+	--
+	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.node ''
+		|| '' SET geom = '' || quote_literal(apoint) 
+		|| '' WHERE node_id = '' || anode;
+
+	RETURN ''Isolated Node '' || anode || '' moved to location ''
+		|| x(apoint) || '','' || y(apoint);
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_MoveIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.3 
+--
+--  ST_RemoveIsoNode(atopology, anode)
+--
+CREATEFUNCTION topology.ST_RemoveIsoNode(varchar, integer)
+	RETURNS TEXT AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anode ALIAS FOR $2;
+	rec RECORD;
+BEGIN
+
+	--
+	-- Atopology and apoint are required
+	-- 
+	IF atopology IS NULL OR anode IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Check node isolation.
+	-- 
+	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' ||
+		'' WHERE start_node =  '' || anode ||
+		'' OR end_node = '' || anode 
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - not isolated node'';
+	END LOOP;
+
+	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.node ''
+		|| '' WHERE node_id = '' || anode;
+
+	RETURN ''Isolated node '' || anode || '' removed'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_RemoveIsoNode
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.7 
+--
+--  ST_RemoveIsoEdge(atopology, anedge)
+--
+CREATEFUNCTION topology.ST_RemoveIsoEdge(varchar, integer)
+	RETURNS TEXT AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anedge ALIAS FOR $2;
+	edge RECORD;
+	rec RECORD;
+	ok BOOL;
+BEGIN
+
+	--
+	-- Atopology and anedge are required
+	-- 
+	IF atopology IS NULL OR anedge IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Check node existance
+	-- 
+	ok = false;
+	FOR edge IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' ||
+		'' WHERE edge_id =  '' || anedge
+	LOOP
+		ok = true;
+	END LOOP;
+	IF NOT ok THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - non-existent edge'';
+	END IF;
+
+	--
+	-- Check node isolation
+	-- 
+	IF edge.left_face != edge.right_face THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - not isolated edge'';
+	END IF;
+
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' 
+		|| '' WHERE edge_id !=  '' || anedge
+		|| '' AND ( start_node = '' || edge.start_node
+		|| '' OR start_node = '' || edge.end_node
+		|| '' OR end_node = '' || edge.start_node
+		|| '' OR end_node = '' || edge.end_node
+		|| '' ) ''
+	LOOP
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - not isolated edge'';
+	END LOOP;
+
+	--
+	-- Delete the edge
+	--
+	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
+		|| '' WHERE edge_id = '' || anedge;
+
+	RETURN ''Isolated edge '' || anedge || '' removed'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_RemoveIsoEdge
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.8 
+--
+--  ST_NewEdgesSplit(atopology, anedge, apoint)
+--
+CREATEFUNCTION topology.ST_NewEdgesSplit(varchar, integer, geometry)
+	RETURNS INTEGER AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anedge ALIAS FOR $2;
+	apoint ALIAS FOR $3;
+	oldedge RECORD;
+	rec RECORD;
+	tmp integer;
+	topoid integer;
+	nodeid integer;
+	nodepos float8;
+	edgeid1 integer;
+	edgeid2 integer;
+	edge1 geometry;
+	edge2 geometry;
+	ok BOOL;
+BEGIN
+
+	--
+	-- All args required
+	-- 
+	IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Check node existance
+	-- 
+	ok = false;
+	FOR oldedge IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' ||
+		'' WHERE edge_id =  '' || anedge
+	LOOP
+		ok = true;
+	END LOOP;
+	IF NOT ok THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - non-existent edge'';
+	END IF;
+
+	--
+	-- Check that given point is Within(anedge.geom)
+	-- 
+	IF NOT within(apoint, oldedge.geom) THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - point not on edge'';
+	END IF;
+
+	--
+	-- Check if a coincident node already exists
+	--
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node '' ||
+		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - coincident node'';
+	END LOOP;
+
+	--
+	-- Get new node id
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.node_node_id_seq'''')''
+	LOOP
+		nodeid = rec.nextval;
+	END LOOP;
+
+	--RAISE NOTICE ''Next node id = % '', nodeid;
+
+	--
+	-- Add the new node 
+	--
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.node(node_id, geom) 
+		VALUES(''||nodeid||'',''||quote_literal(apoint)||
+		'')'';
+
+	--
+	-- Delete the old edge
+	--
+	EXECUTE ''DELETE FROM '' || quote_ident(atopology) || ''.edge_data ''
+		|| '' WHERE edge_id = '' || anedge;
+
+	--
+	-- Compute new edges
+	--
+	nodepos = line_locate_point(oldedge.geom, apoint);
+	edge1 = line_substring(oldedge.geom, 0, nodepos);
+	edge2 = line_substring(oldedge.geom, nodepos, 1);
+
+	--
+	-- Get ids for the new edges 
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.edge_data_edge_id_seq'''')''
+	LOOP
+		edgeid1 = rec.nextval;
+	END LOOP;
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.edge_data_edge_id_seq'''')''
+	LOOP
+		edgeid2 = rec.nextval;
+	END LOOP;
+
+	--RAISE NOTICE ''EdgeId1 % EdgeId2 %'', edgeid1, edgeid2;
+
+	--
+	-- Insert the two new edges
+	--
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.edge VALUES(''
+		||edgeid1||'',''||oldedge.start_node
+		||'',''||nodeid
+		||'',''||edgeid2
+		||'',''||oldedge.next_right_edge
+		||'',''||oldedge.left_face
+		||'',''||oldedge.right_face
+		||'',''||quote_literal(edge1)
+		||'')'';
+
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.edge VALUES(''
+		||edgeid2||'',''||nodeid
+		||'',''||oldedge.end_node
+		||'',''||oldedge.next_left_edge
+		||'',-''||edgeid1
+		||'',''||oldedge.left_face
+		||'',''||oldedge.right_face
+		||'',''||quote_literal(edge2)
+		||'')'';
+
+	--
+	-- Update all next edge references to match new layout
+	--
+
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET next_right_edge = ''
+		|| edgeid2
+		|| '',''
+		|| '' abs_next_right_edge = '' || edgeid2
+		|| '' WHERE next_right_edge = '' || anedge;
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET next_right_edge = ''
+		|| -edgeid1
+		|| '',''
+		|| '' abs_next_right_edge = '' || edgeid1
+		|| '' WHERE next_right_edge = '' || -anedge;
+
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET next_left_edge = ''
+		|| edgeid1
+		|| '',''
+		|| '' abs_next_left_edge = '' || edgeid1
+		|| '' WHERE next_left_edge = '' || anedge;
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET ''
+		|| '' next_left_edge = '' || -edgeid2
+		|| '',''
+		|| '' abs_next_left_edge = '' || edgeid2
+		|| '' WHERE next_left_edge = '' || -anedge;
+
+	-- Get topology id
+        SELECT id FROM topology.topology into topoid
+                WHERE name = atopology;
+
+	--
+	-- Update references in the Relation table.
+	-- We only take into considerations non-hierarchical
+	-- TopoGeometry here, for obvious reasons.
+	--
+	FOR rec IN EXECUTE ''SELECT r.* FROM ''
+		|| quote_ident(atopology)
+		|| ''.relation r, topology.layer l ''
+		|| '' WHERE ''
+		|| '' l.topology_id = '' || topoid
+		|| '' AND l.level = 0 ''
+		|| '' AND l.layer_id = r.layer_id ''
+		|| '' AND abs(r.element_id) = '' || anedge
+		|| '' AND r.element_type = 2''
+	LOOP
+		--RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split'', rec.topogeo_id, rec.layer_id;
+
+		-- Delete old reference
+		EXECUTE ''DELETE FROM '' || quote_ident(atopology)
+			|| ''.relation ''
+			|| '' WHERE ''
+			|| ''layer_id = '' || rec.layer_id
+			|| '' AND ''
+			|| ''topogeo_id = '' || rec.topogeo_id
+			|| '' AND ''
+			|| ''element_type = '' || rec.element_type
+			|| '' AND ''
+			|| ''abs(element_id) = '' || anedge;
+
+		-- Add new reference to edge1
+		IF rec.element_id < 0 THEN
+			tmp = -edgeid1;
+		ELSE
+			tmp = edgeid1;
+		END IF;
+		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+			|| ''.relation ''
+			|| '' VALUES( ''
+			|| rec.topogeo_id
+			|| '',''
+			|| rec.layer_id
+			|| '',''
+			|| tmp
+			|| '',''
+			|| rec.element_type
+			|| '')'';
+
+		-- Add new reference to edge2
+		IF rec.element_id < 0 THEN
+			tmp = -edgeid2;
+		ELSE
+			tmp = edgeid2;
+		END IF;
+		EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+			|| ''.relation ''
+			|| '' VALUES( ''
+			|| rec.topogeo_id
+			|| '',''
+			|| rec.layer_id
+			|| '',''
+			|| tmp
+			|| '',''
+			|| rec.element_type
+			|| '')'';
+			
+	END LOOP;
+
+	--RAISE NOTICE ''Edge % split in edges % and % by node %'',
+	--	anedge, edgeid1, edgeid2, nodeid;
+
+	RETURN nodeid; 
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_NewEdgesSplit
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.9 
+--
+--  ST_ModEdgesSplit(atopology, anedge, apoint)
+--
+CREATEFUNCTION topology.ST_ModEdgesSplit(varchar, integer, geometry)
+	RETURNS INTEGER AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anedge ALIAS FOR $2;
+	apoint ALIAS FOR $3;
+	oldedge RECORD;
+	rec RECORD;
+	tmp integer;
+	topoid integer;
+	nodeid integer;
+	nodepos float8;
+	newedgeid integer;
+	newedge1 geometry;
+	newedge2 geometry;
+	query text;
+	ok BOOL;
+BEGIN
+
+	--
+	-- All args required
+	-- 
+	IF atopology IS NULL OR anedge IS NULL OR apoint IS NULL THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Check node existance
+	-- 
+	ok = false;
+	FOR oldedge IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' ||
+		'' WHERE edge_id =  '' || anedge
+	LOOP
+		ok = true;
+	END LOOP;
+	IF NOT ok THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - non-existent edge'';
+	END IF;
+
+	--
+	-- Check that given point is Within(anedge.geom)
+	-- 
+	IF NOT within(apoint, oldedge.geom) THEN
+		RAISE EXCEPTION
+		  ''SQL/MM Spatial exception - point not on edge'';
+	END IF;
+
+	--
+	-- Check if a coincident node already exists
+	--
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node '' ||
+		''WHERE geom && '' || quote_literal(apoint) || ''::geometry''
+		||'' AND x(geom) = x(''||quote_literal(apoint)||''::geometry)''
+		||'' AND y(geom) = y(''||quote_literal(apoint)||''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - coincident node'';
+	END LOOP;
+
+	--
+	-- Get new node id
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.node_node_id_seq'''')''
+	LOOP
+		nodeid = rec.nextval;
+	END LOOP;
+
+	--RAISE NOTICE ''Next node id = % '', nodeid;
+
+	--
+	-- Add the new node 
+	--
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.node(node_id, geom) 
+		VALUES(''||nodeid||'',''||quote_literal(apoint)||
+		'')'';
+
+	--
+	-- Compute new edge
+	--
+	nodepos = line_locate_point(oldedge.geom, apoint);
+	newedge1 = line_substring(oldedge.geom, 0, nodepos);
+	newedge2 = line_substring(oldedge.geom, nodepos, 1);
+
+
+	--
+	-- Get ids for the new edge
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.edge_data_edge_id_seq'''')''
+	LOOP
+		newedgeid = rec.nextval;
+	END LOOP;
+
+	--
+	-- Insert the new edge
+	--
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.edge ''
+		|| ''(edge_id, start_node, end_node,''
+		|| ''next_left_edge, next_right_edge,''
+		|| ''left_face, right_face, geom) ''
+		|| ''VALUES(''
+		||newedgeid||'',''||nodeid
+		||'',''||oldedge.end_node
+		||'',''||oldedge.next_left_edge
+		||'',-''||anedge
+		||'',''||oldedge.left_face
+		||'',''||oldedge.right_face
+		||'',''||quote_literal(newedge2)
+		||'')'';
+
+	--
+	-- Update the old edge
+	--
+	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
+		|| '' SET geom = '' || quote_literal(newedge1)
+		|| '',''
+		|| '' next_left_edge = '' || newedgeid
+		|| '',''
+		|| '' end_node = '' || nodeid
+		|| '' WHERE edge_id = '' || anedge;
+
+
+	--
+	-- Update all next edge references to match new layout
+	--
+
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET next_right_edge = ''
+		|| -newedgeid 
+		|| '',''
+		|| '' abs_next_right_edge = '' || newedgeid
+		|| '' WHERE next_right_edge = '' || -anedge;
+
+	EXECUTE ''UPDATE '' || quote_ident(atopology)
+		|| ''.edge_data SET ''
+		|| '' next_left_edge = '' || -newedgeid
+		|| '',''
+		|| '' abs_next_left_edge = '' || newedgeid
+		|| '' WHERE next_left_edge = '' || -anedge;
+
+	-- Get topology id
+        SELECT id FROM topology.topology into topoid
+                WHERE name = atopology;
+
+	--
+	-- Update references in the Relation table.
+	-- We only take into considerations non-hierarchical
+	-- TopoGeometry here, for obvious reasons.
+	--
+	FOR rec IN EXECUTE ''SELECT r.* FROM ''
+		|| quote_ident(atopology)
+		|| ''.relation r, topology.layer l ''
+		|| '' WHERE ''
+		|| '' l.topology_id = '' || topoid
+		|| '' AND l.level = 0 ''
+		|| '' AND l.layer_id = r.layer_id ''
+		|| '' AND abs(r.element_id) = '' || anedge
+		|| '' AND r.element_type = 2''
+	LOOP
+		--RAISE NOTICE ''TopoGeometry % in layer % contains the edge being split (%) - updating to add new edge %'', rec.topogeo_id, rec.layer_id, anedge, newedgeid;
+
+		-- Add new reference to edge1
+		IF rec.element_id < 0 THEN
+			tmp = -newedgeid;
+		ELSE
+			tmp = newedgeid;
+		END IF;
+		query = ''INSERT INTO '' || quote_ident(atopology)
+			|| ''.relation ''
+			|| '' VALUES( ''
+			|| rec.topogeo_id
+			|| '',''
+			|| rec.layer_id
+			|| '',''
+			|| tmp
+			|| '',''
+			|| rec.element_type
+			|| '')'';
+
+		--RAISE NOTICE ''%'', query;
+		EXECUTE query;
+	END LOOP;
+
+	--RAISE NOTICE ''Edge % split in edges % and % by node %'',
+	--	anedge, anedge, newedgeid, nodeid;
+
+	RETURN nodeid; 
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_ModEdgesSplit
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.4 
+--
+--  ST_AddIsoEdge(atopology, anode, anothernode, acurve)
+-- 
+CREATEFUNCTION topology.ST_AddIsoEdge(varchar, integer, integer, geometry)
+	RETURNS INTEGER AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anode ALIAS FOR $2;
+	anothernode ALIAS FOR $3;
+	acurve ALIAS FOR $4;
+	aface INTEGER;
+	face GEOMETRY;
+	snodegeom GEOMETRY;
+	enodegeom GEOMETRY;
+	count INTEGER;
+	rec RECORD;
+	edgeid INTEGER;
+BEGIN
+
+	--
+	-- All arguments required
+	-- 
+	IF atopology IS NULL
+	   OR anode IS NULL
+	   OR anothernode IS NULL
+	   OR acurve IS NULL
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Acurve must be a LINESTRING
+	--
+	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - invalid curve'';
+	END IF;
+
+	--
+	-- Acurve must be a simple
+	--
+	IF NOT issimple(acurve)
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - curve not simple'';
+	END IF;
+
+	--
+	-- Check for:
+	--    existence of nodes
+	--    nodes faces match
+	-- Extract:
+	--    nodes face id
+	--    nodes geoms
+	--
+	aface := NULL;
+	count := 0;
+	FOR rec IN EXECUTE ''SELECT geom, containing_face, node_id FROM ''
+		|| quote_ident(atopology) || ''.node
+		WHERE node_id = '' || anode ||
+		'' OR node_id = '' || anothernode
+	LOOP 
+		IF count > 0 AND aface != rec.containing_face THEN
+			RAISE EXCEPTION
+			''SQL/MM Spatial exception - nodes in different faces'';
+		ELSE
+			aface := rec.containing_face;
+		END IF;
+
+		-- Get nodes geom
+		IF rec.node_id = anode THEN
+			snodegeom = rec.geom;
+		ELSE
+			enodegeom = rec.geom;
+		END IF;
+
+		count = count+1;
+
+	END LOOP;
+	IF count < 2 THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - non-existent node'';
+	END IF;
+
+
+	--
+	-- Check nodes isolation.
+	-- 
+	FOR rec IN EXECUTE ''SELECT edge_id FROM ''
+		|| quote_ident(atopology) || ''.edge_data '' ||
+		'' WHERE start_node =  '' || anode ||
+		'' OR end_node = '' || anode ||
+		'' OR start_node = '' || anothernode ||
+		'' OR end_node = '' || anothernode
+	LOOP
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - not isolated node'';
+	END LOOP;
+
+	--
+	-- Check acurve to be within endpoints containing face 
+	-- (unless it is the world face, I suppose)
+	-- 
+	IF aface IS NOT NULL THEN
+
+		--
+		-- Extract endpoints face geometry
+		--
+		FOR rec IN EXECUTE ''SELECT topology.ST_GetFaceGeometry(''
+			|| quote_literal(atopology) ||
+			'','' || aface || '') as face''
+		LOOP
+			face := rec.face;
+		END LOOP;
+
+		--
+		-- Check acurve to be within face
+		--
+		IF ! within(acurve, face) THEN
+	RAISE EXCEPTION
+	''SQL/MM Spatial exception - geometry not within face.'';
+		END IF;
+
+	END IF;
+
+	--
+	-- l) Check that start point of acurve match start node
+	-- geoms.
+	-- 
+	IF x(snodegeom) != x(StartPoint(acurve)) OR
+	   y(snodegeom) != y(StartPoint(acurve)) THEN
+  RAISE EXCEPTION
+  ''SQL/MM Spatial exception - start node not geometry start point.'';
+	END IF;
+
+	--
+	-- m) Check that end point of acurve match end node
+	-- geoms.
+	-- 
+	IF x(enodegeom) != x(EndPoint(acurve)) OR
+	   y(enodegeom) != y(EndPoint(acurve)) THEN
+  RAISE EXCEPTION
+  ''SQL/MM Spatial exception - end node not geometry end point.'';
+	END IF;
+
+	--
+	-- n) Check if curve crosses (contains) any node
+	-- I used _contains_ here to leave endpoints out
+	-- 
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node ''
+		|| '' WHERE geom && '' || quote_literal(acurve) 
+		|| '' AND contains('' || quote_literal(acurve)
+		|| '',geom)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry crosses a node'';
+	END LOOP;
+
+	--
+	-- o) Check if curve intersects any other edge
+	-- 
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data
+		WHERE geom && '' || quote_literal(acurve) || ''::geometry
+		AND intersects(geom, '' || quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry intersects an edge'';
+	END LOOP;
+
+	--
+	-- Get new edge id from sequence
+	--
+	FOR rec IN EXECUTE ''SELECT nextval('''''' ||
+		atopology || ''.edge_data_edge_id_seq'''')''
+	LOOP
+		edgeid = rec.nextval;
+	END LOOP;
+
+	--
+	-- Insert the new row
+	--
+	IF aface IS NULL THEN aface := 0; END IF;
+
+	EXECUTE ''INSERT INTO '' || quote_ident(atopology)
+		|| ''.edge VALUES(''||edgeid||'',''||anode||
+			'',''||anothernode||'',''
+			||(-edgeid)||'',''||edgeid||'',''
+			||aface||'',''||aface||'',''
+			||quote_literal(acurve)||'')'';
+
+	RETURN edgeid;
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddIsoEdge
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.6
+--
+--  ST_ChangeEdgeGeom(atopology, anedge, acurve)
+-- 
+CREATEFUNCTION topology.ST_ChangeEdgeGeom(varchar, integer, geometry)
+	RETURNS TEXT AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anedge ALIAS FOR $2;
+	acurve ALIAS FOR $3;
+	aface INTEGER;
+	face GEOMETRY;
+	snodegeom GEOMETRY;
+	enodegeom GEOMETRY;
+	count INTEGER;
+	rec RECORD;
+	edgeid INTEGER;
+	oldedge RECORD;
+BEGIN
+
+	--
+	-- All arguments required
+	-- 
+	IF atopology IS NULL
+	   OR anedge IS NULL
+	   OR acurve IS NULL
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Acurve must be a LINESTRING
+	--
+	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - invalid curve'';
+	END IF;
+
+	--
+	-- Acurve must be a simple
+	--
+	IF NOT issimple(acurve)
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - curve not simple'';
+	END IF;
+
+	--
+	-- e) Check StartPoint consistency
+	--
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data e, ''
+		|| quote_ident(atopology) || ''.node n ''
+		|| '' WHERE e.edge_id = '' || anedge
+		|| '' AND n.node_id = e.start_node ''
+		|| '' AND ( x(n.geom) != ''
+		|| x(StartPoint(acurve))
+		|| '' OR y(n.geom) != ''
+		|| y(StartPoint(acurve))
+		|| '')''
+	LOOP 
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - start node not geometry start point.'';
+	END LOOP;
+
+	--
+	-- f) Check EndPoint consistency
+	--
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data e, ''
+		|| quote_ident(atopology) || ''.node n ''
+		|| '' WHERE e.edge_id = '' || anedge
+		|| '' AND n.node_id = e.end_node ''
+		|| '' AND ( x(n.geom) != ''
+		|| x(EndPoint(acurve))
+		|| '' OR y(n.geom) != ''
+		|| y(EndPoint(acurve))
+		|| '')''
+	LOOP 
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - end node not geometry end point.'';
+	END LOOP;
+
+	--
+	-- g) Check if curve crosses any node
+	-- _within_ used to let endpoints out
+	-- 
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node
+		WHERE geom && '' || quote_literal(acurve) || ''::geometry
+		AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry crosses a node'';
+	END LOOP;
+
+	--
+	-- h) Check if curve intersects any other edge
+	-- 
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data ''
+		|| '' WHERE edge_id != '' || anedge
+		|| '' AND geom && ''
+		|| quote_literal(acurve) || ''::geometry ''
+		|| '' AND intersects(geom, ''
+		|| quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry intersects an edge'';
+	END LOOP;
+
+	--
+	-- Update edge geometry
+	--
+	EXECUTE ''UPDATE '' || quote_ident(atopology) || ''.edge_data ''
+		|| '' SET geom = '' || quote_literal(acurve) 
+		|| '' WHERE edge_id = '' || anedge;
+
+	RETURN ''Edge '' || anedge || '' changed'';
+
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_ChangeEdgeGeom
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.12
+--
+--  ST_AddEdgeNewFaces(atopology, anode, anothernode, acurve)
+--
+CREATEFUNCTION topology.ST_AddEdgeNewFaces(varchar, integer, integer, geometry)
+	RETURNS INTEGER AS
+'
+DECLARE
+	atopology ALIAS FOR $1;
+	anode ALIAS FOR $2;
+	anothernode ALIAS FOR $3;
+	acurve ALIAS FOR $4;
+	rec RECORD;
+	i INTEGER;
+	az FLOAT8;
+	azdif FLOAT8;
+	myaz FLOAT8;
+	minazimuth FLOAT8;
+	maxazimuth FLOAT8;
+	p2 GEOMETRY;
+BEGIN
+
+	--
+	-- All args required
+	-- 
+	IF atopology IS NULL
+		OR anode IS NULL
+		OR anothernode IS NULL
+		OR acurve IS NULL
+	THEN
+		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	--
+	-- Acurve must be a LINESTRING
+	--
+	IF substring(geometrytype(acurve), 1, 4) != ''LINE''
+	THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - invalid curve'';
+	END IF;
+	
+	--
+	-- Curve must be simple
+	--
+	IF NOT issimple(acurve) THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - curve not simple'';
+	END IF;
+
+	-- 
+	-- Check endpoints existance and match with Curve geometry
+	--
+	i=0;
+	FOR rec IN EXECUTE ''SELECT ''
+		|| '' CASE WHEN node_id = '' || anode
+		|| '' THEN 1 WHEN node_id = '' || anothernode
+		|| '' THEN 0 END AS start, geom FROM ''
+		|| quote_ident(atopology)
+		|| ''.node ''
+		|| '' WHERE node_id IN ( ''
+		|| anode || '','' || anothernode
+		|| '')''
+	LOOP
+		IF rec.start THEN
+			IF NOT Equals(rec.geom, StartPoint(acurve))
+			THEN
+	RAISE EXCEPTION
+	''SQL/MM Spatial exception - start node not geometry start point.'';
+			END IF;
+		ELSE
+			IF NOT Equals(rec.geom, EndPoint(acurve))
+			THEN
+	RAISE EXCEPTION
+	''SQL/MM Spatial exception - end node not geometry end point.'';
+			END IF;
+		END IF;
+
+		i=i+1;
+	END LOOP;
+
+	IF i < 2 THEN
+		RAISE EXCEPTION
+		 ''SQL/MM Spatial exception - non-existent node'';
+	END IF;
+
+	--
+	-- Check if this geometry crosses any node
+	--
+	FOR rec IN EXECUTE ''SELECT node_id FROM ''
+		|| quote_ident(atopology) || ''.node
+		WHERE geom && '' || quote_literal(acurve) || ''::geometry
+		AND within(geom, '' || quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry crosses a node'';
+	END LOOP;
+
+	--
+	-- Check if this geometry crosses any existing edge
+	--
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data
+		WHERE geom && '' || quote_literal(acurve) || ''::geometry
+		AND crosses(geom, '' || quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - geometry crosses an edge'';
+	END LOOP;
+
+	--
+	-- Check if another edge share this edge endpoints
+	--
+	FOR rec IN EXECUTE ''SELECT * FROM ''
+		|| quote_ident(atopology) || ''.edge_data ''
+		|| '' WHERE ''
+		|| '' geom && '' || quote_literal(acurve) || ''::geometry ''
+		|| '' AND ''
+		|| '' ( (''
+		|| '' start_node = '' || anode
+		|| '' AND ''
+		|| '' end_node = '' || anothernode
+		|| '' ) OR ( ''
+		|| '' end_node = '' || anode
+		|| '' AND ''
+		|| '' start_node = '' || anothernode
+		|| '' ) )''
+		|| '' AND ''
+		|| ''equals(geom,'' || quote_literal(acurve) || ''::geometry)''
+	LOOP
+		RAISE EXCEPTION
+		''SQL/MM Spatial exception - coincident edge'';
+	END LOOP;
+
+	---------------------------------------------------------------
+	--
+	-- All checks passed, time to extract informations about
+	-- endpoints:
+	--
+	--      next_left_edge
+	--      next_right_edge
+	--	left_face
+	--	right_face
+	--
+	---------------------------------------------------------------
+
+	--
+	--
+	-- Compute next_left_edge 
+	--
+	-- We fetch all edges with an endnode equal to
+	-- this edge end_node (anothernode).
+	-- For each edge we compute azimuth of the segment(s).
+	-- Of interest are the edges with closest (smaller
+	-- and bigger) azimuths then the azimuth of
+	-- this edge last segment.
+	--
+
+	myaz = azimuth(EndPoint(acurve), PointN(acurve, NumPoints(acurve)-1));
+	RAISE NOTICE ''My end-segment azimuth: %'', myaz;
+	FOR rec IN EXECUTE ''SELECT ''
+		|| ''edge_id, end_node, start_node, geom''
+		|| '' FROM ''
+		|| quote_ident(atopology)
+		|| ''.edge_data ''
+		|| '' WHERE ''
+		|| '' end_node = '' || anothernode
+		|| '' OR ''
+		|| '' start_node = '' || anothernode
+	LOOP
+
+		IF rec.start_node = anothernode THEN
+			--
+			-- Edge starts at our node, we compute
+			-- azimuth from node to its second point
+			--
+			az = azimuth(EndPoint(acurve),
+				PointN(rec.geom, 2));
+
+			RAISE NOTICE ''Edge % starts at node % - azimuth %'',
+				rec.edge_id, rec.start_node, az;
+		END IF;
+
+		IF rec.end_node = anothernode THEN
+			--
+			-- Edge ends at our node, we compute
+			-- azimuth from node to its second-last point
+			--
+			az = azimuth(EndPoint(acurve),
+				PointN(rec.geom, NumPoints(rec.geom)-1));
+
+			RAISE NOTICE ''Edge % ends at node % - azimuth %'',
+				rec.edge_id, rec.end_node, az;
+		END IF;
+	END LOOP;
+
+
+	RAISE EXCEPTION ''Not implemented yet'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_AddEdgeNewFaces
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.17
+--
+--  ST_InitTopoGeo(atopology)
+--
+CREATEFUNCTION topology.ST_InitTopoGeo(varchar)
+RETURNS text
+AS '
+DECLARE
+	atopology alias for $1;
+	rec RECORD;
+	topology_id numeric;
+BEGIN
+	IF atopology IS NULL THEN
+		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
+	LOOP
+		RAISE EXCEPTION ''SQL/MM Spatial exception - schema already exists'';
+	END LOOP;
+
+	FOR rec IN EXECUTE ''SELECT topology.CreateTopology(''
+		||quote_literal(atopology)|| '') as id''
+	LOOP
+		topology_id := rec.id;
+	END LOOP;
+
+	RETURN ''Topology-Geometry '' || quote_literal(atopology)
+		|| '' (id:'' || topology_id || '') created. '';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_InitTopoGeo
+
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.18
+--
+--  ST_CreateTopoGeo(atopology, acollection)
+--
+CREATEFUNCTION topology.ST_CreateTopoGeo(varchar, geometry)
+RETURNS text
+AS '
+DECLARE
+	atopology alias for $1;
+	acollection alias for $2;
+	typ char(4);
+	rec RECORD;
+	ret int;
+	schemaoid oid;
+BEGIN
+	IF atopology IS NULL OR acollection IS NULL THEN
+		RAISE EXCEPTION ''SQL/MM Spatial exception - null argument'';
+	END IF;
+
+	-- Verify existance of the topology schema 
+	FOR rec in EXECUTE ''SELECT oid,count(*) FROM pg_namespace WHERE ''
+		|| '' nspname = '' || quote_literal(atopology)
+		|| '' GROUP BY oid''
+		
+	LOOP
+		IF rec.count < 1 THEN
+	RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent schema'';
+		END IF;
+		schemaoid := rec.oid;
+	END LOOP;
+
+	-- Verify existance of the topology views in the topology schema 
+	FOR rec in EXECUTE ''SELECT count(*) FROM pg_class WHERE ''
+		|| '' relnamespace = '' || schemaoid 
+		|| '' and relname = ''''node''''''
+		|| '' OR relname = ''''edge''''''
+		|| '' OR relname = ''''face''''''
+	LOOP
+		IF rec.count < 3 THEN
+	RAISE EXCEPTION ''SQL/MM Spatial exception - non-existent view'';
+		END IF;
+	END LOOP;
+
+	-- Verify the topology views in the topology schema to be empty
+	FOR rec in EXECUTE
+		''SELECT count(*) FROM ''
+		|| quote_ident(atopology) || ''.edge_data ''
+		|| '' UNION '' ||
+		''SELECT count(*) FROM ''
+		|| quote_ident(atopology) || ''.node ''
+	LOOP
+		IF rec.count > 0 THEN
+	RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty view'';
+		END IF;
+	END LOOP;
+
+	-- face check is separated as it will contain a single (world)
+	-- face record
+	FOR rec in EXECUTE
+		''SELECT count(*) FROM ''
+		|| quote_ident(atopology) || ''.face ''
+	LOOP
+		IF rec.count != 1 THEN
+	RAISE EXCEPTION ''SQL/MM Spatial exception - non-empty face view'';
+		END IF;
+	END LOOP;
+
+	-- 
+	-- LOOP through the elements invoking the specific function
+	-- 
+	FOR rec IN SELECT geom(dump(acollection))
+	LOOP
+		typ := substring(geometrytype(rec.geom), 1, 3);
+
+		IF typ = ''LIN'' THEN
+	SELECT topology.TopoGeo_addLinestring(atopology, rec.geom) INTO ret;
+		ELSIF typ = ''POI'' THEN
+	SELECT topology.TopoGeo_AddPoint(atopology, rec.geom) INTO ret;
+		ELSIF typ = ''POL'' THEN
+	SELECT topology.TopoGeo_AddPolygon(atopology, rec.geom) INTO ret;
+		ELSE
+	RAISE EXCEPTION ''ST_CreateTopoGeo got unknown geometry type: %'', typ;
+		END IF;
+
+	END LOOP;
+
+	RETURN ''Topology '' || atopology || '' populated'';
+
+	RAISE EXCEPTION ''ST_CreateTopoGeo not implemente yet'';
+END
+'
+LANGUAGE 'plpgsql' _VOLATILE;
+--} ST_CreateTopoGeo
+
+--=}  SQL/MM block
+
+COMMIT;
+



More information about the postgis-commits mailing list