/*

 spatialite.c -- SQLite3 spatial extension

 version 4.3, 2015 June 29

 Author: Sandro Furieri a.furieri@lqt.it

 ------------------------------------------------------------------------------
 
 Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
 The contents of this file are subject to the Mozilla Public License Version
 1.1 (the "License"); you may not use this file except in compliance with
 the License. You may obtain a copy of the License at
 http://www.mozilla.org/MPL/
 
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

The Original Code is the SpatiaLite library

The Initial Developer of the Original Code is Alessandro Furieri
 
Portions created by the Initial Developer are Copyright (C) 2008-2015
the Initial Developer. All Rights Reserved.

Contributor(s):
Pepijn Van Eeckhoudt <pepijnvaneeckhoudt@luciad.com>
(implementing Android support)

Mark Johnson <mj10777@googlemail.com>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
 
*/

/*
 
CREDITS:

this module has been partly funded by:
Regione Toscana - Settore Sistema Informativo Territoriale ed Ambientale
(exposing liblwgeom APIs as SpatiaLite own SQL functions) 

*/

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <locale.h>

#if defined(_WIN32) && !defined(__MINGW32__)
#include <io.h>
#include <direct.h>
#else
#include <dirent.h>
#endif

#if defined(_WIN32) && !defined(__MINGW32__)
#include "config-msvc.h"
#else
#include "config.h"
#endif

#if defined(_WIN32) || defined(WIN32)
#include <io.h>
#define isatty	_isatty
#else
#include <unistd.h>
#endif


#include <spatialite/sqlite.h>
#include <spatialite/debug.h>

#include <spatialite/gaiaaux.h>
#include <spatialite/gaiageo.h>
#include <spatialite/gaiaexif.h>
#include <spatialite/geopackage.h>
#include <spatialite/spatialite.h>
#include <spatialite/gg_advanced.h>
#include <spatialite/gg_dxf.h>
#include <spatialite/gaiamatrix.h>
#include <spatialite/geopackage.h>
#include <spatialite/control_points.h>
#include <spatialite/stored_procedures.h>
#include <spatialite.h>
#include <spatialite_private.h>

#ifdef ENABLE_LIBXML2		/* LIBXML2 (and thus WFS) enabled */
#include <spatialite/gg_wfs.h>
#endif

#ifndef OMIT_FREEXL		/* including FreeXL */
#include <freexl.h>
#endif

#ifndef OMIT_GEOS		/* including GEOS */
#ifdef GEOS_REENTRANT
#ifdef GEOS_ONLY_REENTRANT
#define GEOS_USE_ONLY_R_API	/* only fully thread-safe GEOS API */
#endif
#endif
#include <geos_c.h>
#endif

#ifndef OMIT_PROJ		/* including PROJ.4 */
#include <proj_api.h>
#endif

#ifdef _WIN32
#define strcasecmp	_stricmp
#endif /* not WIN32 */

/* 64 bit integer: portable format for printf() */
#if defined(_WIN32) && !defined(__MINGW32__)
#define FRMT64 "%I64d"
#define FRMT64_WO_PCT "I64d"
#else
#define FRMT64 "%lld"
#define FRMT64_WO_PCT "lld"
#endif

#define GAIA_UNUSED() if (argc || argv) argc = argc;

struct gaia_geom_chain_item
{
/* a struct used to store a chain item */
    gaiaGeomCollPtr geom;
    struct gaia_geom_chain_item *next;
};

struct gaia_geom_chain
{
/* a struct used to store a dynamic chain of GeometryCollections */
    int all_polygs;
    struct gaia_geom_chain_item *first;
    struct gaia_geom_chain_item *last;
};

#ifndef OMIT_GEOCALLBACKS	/* supporting RTree geometry callbacks */
struct gaia_rtree_mbr
{
/* a struct used by R*Tree GeometryCallback functions [MBR] */
    double minx;
    double miny;
    double maxx;
    double maxy;
};
#endif /* end RTree geometry callbacks */

struct stddev_str
{
/* a struct to implement StandardVariation and Variance aggregate functions */
    int cleaned;
    double mean;
    double quot;
    double count;
};

struct string_list_str
{
/* a struct supporting MakeStringList aggregate function */
    char *string;
    char separator;
};

struct fdo_table
{
/* a struct to implement a linked-list for FDO-ORG table names */
    char *table;
    struct fdo_table *next;
};

struct gpkg_table
{
/* a struct to implement a linked-list for OGC GeoPackage table names */
    char *table;
    struct gpkg_table *next;
};


/*
************************************************************************
**
** the following code has been integrally copied from SQLite's own sources:
** -/ext/misc/eval.c
*/

/*
** 2014-11-10
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite extension implements SQL function eval() which runs
** SQL statements recursively.
*/

struct EvalResult
{
/*
** Structure used to accumulate the output
*/
    char *z;			/* Accumulated output */
    const char *zSep;		/* Separator */
    int szSep;			/* Size of the separator string */
    unsigned int nAlloc;	/* Number of bytes allocated for z[] */
    int nUsed;			/* Number of bytes of z[] actually used */
};

static int
eval_callback (void *pCtx, int argc, char **argv, char **colnames)
{
/*
** Callback from sqlite_exec() for the eval() function.
*/
    struct EvalResult *p = (struct EvalResult *) pCtx;
    int i;

    if (colnames == NULL)
	colnames = NULL;	/* silencing stupid compiler warnings */

    for (i = 0; i < argc; i++)
      {
	  const char *z = argv[i] ? argv[i] : "";
	  size_t sz = strlen (z);
	  if (sz + p->nUsed + p->szSep + 1 > p->nAlloc)
	    {
		char *zNew;
		p->nAlloc = p->nAlloc * 2 + sz + p->szSep + 1;
		zNew = sqlite3_realloc (p->z, p->nAlloc);
		if (zNew == 0)
		  {
		      sqlite3_free (p->z);
		      memset (p, 0, sizeof (*p));
		      return 1;
		  }
		p->z = zNew;
	    }
	  if (p->nUsed > 0)
	    {
		memcpy (&p->z[p->nUsed], p->zSep, p->szSep);
		p->nUsed += p->szSep;
	    }
	  memcpy (&p->z[p->nUsed], z, sz);
	  p->nUsed += sz;
      }
    return 0;
}

static void
fnct_EvalFunc (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/*
** Implementation of the eval(X) and eval(X,Y) SQL functions.
**
** Evaluate the SQL text in X.  Return the results, using string
** Y as the separator.  If Y is omitted, use a single space character.
*/
    const char *zSql;
    sqlite3 *db;
    char *zErr = 0;
    int rc;
    struct EvalResult x;

    memset (&x, 0, sizeof (x));
    x.zSep = " ";
    zSql = (const char *) sqlite3_value_text (argv[0]);
    if (zSql == 0)
	return;
    if (argc > 1)
      {
	  x.zSep = (const char *) sqlite3_value_text (argv[1]);
	  if (x.zSep == 0)
	      return;
      }
    x.szSep = (int) strlen (x.zSep);
    db = sqlite3_context_db_handle (context);
    rc = sqlite3_exec (db, zSql, eval_callback, &x, &zErr);
    if (rc != SQLITE_OK)
      {
	  sqlite3_result_error (context, zErr, -1);
	  sqlite3_free (zErr);
      }
    else if (x.zSep == 0)
      {
	  sqlite3_result_error_nomem (context);
	  sqlite3_free (x.z);
      }
    else
      {
	  sqlite3_result_text (context, x.z, x.nUsed, sqlite3_free);
      }
}

static void
fnct_spatialite_version (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ spatialite_version()
/
/ return a text string representing the current SpatiaLite version
*/
    int len;
    const char *p_result = spatialite_version ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
}

static void
fnct_spatialite_target_cpu (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ spatialite_target_cpu()
/
/ return a text string representing the current SpatiaLite Target CPU
*/
    int len;
    const char *p_result = spatialite_target_cpu ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
}

static void
fnct_freexl_version (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ freexl_version()
/
/ return a text string representing the current FreeXL version
/ or NULL if FreeXL is currently unsupported
*/

#ifndef OMIT_FREEXL		/* FreeXL version */
    int len;
    const char *p_result = freexl_version ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
#else
    sqlite3_result_null (context);
#endif
}

static void
fnct_geos_version (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ geos_version()
/
/ return a text string representing the current GEOS version
/ or NULL if GEOS is currently unsupported
*/

#ifndef OMIT_GEOS		/* GEOS version */
    int len;
    const char *p_result = GEOSversion ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
#else
    sqlite3_result_null (context);
#endif
}

static void
fnct_proj4_version (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ proj4_version()
/
/ return a text string representing the current PROJ.4 version
/ or NULL if PROJ.4 is currently unsupported
*/

#ifndef OMIT_PROJ		/* PROJ.4 version */
    int len;
    const char *p_result = pj_get_release ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
#else
    sqlite3_result_null (context);
#endif
}

static void
fnct_has_proj (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasProj()
/
/ return 1 if built including Proj.4; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_PROJ
#if defined(PJ_VERSION) && PJ_VERSION >= 490
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_proj_geodesic (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ HasProjGeodesic()
/
/ return 1 if built supporting Proj.4 Geodesic; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_PROJ		/* PROJ.4 is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geos (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasGeos()
/
/ return 1 if built including GEOS; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_GEOS		/* GEOS is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geos_advanced (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ HasGeosAdvanced()
/
/ return 1 if built including GEOS-ADVANCED; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef GEOS_ADVANCED		/* GEOS-ADVANCED is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geos_trunk (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasGeosTrunk()
/
/ return 1 if built including GEOS-TRUNK; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_int (context, 0);
}

static void
fnct_has_geos_reentrant (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ HasGeosReentrant()
/
/ return 1 if built including GEOS-REENTRANT; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef GEOS_REENTRANT		/* GEOS-REENTRANT is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geos_only_reentrant (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ HasGeosOnlyReentrant()
/
/ return 1 if built including GEOS-ONLY_REENTRANT; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef GEOS_REENTRANT		/* GEOS-REENTRANT is supported */
#ifdef GEOS_ONLY_REENTRANT	/* GEOS-ONLY-REENTRANT is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_rttopo_version (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ rttopo_version()
/
/ return a text string representing the current RTTOPO version
/ or NULL if RTTOPO is currently unsupported
*/

#ifdef ENABLE_RTTOPO		/* RTTOPO version */
    int len;
    const char *p_result = splite_rttopo_version ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, SQLITE_TRANSIENT);
#else
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_null (context);
#endif
}

static void
fnct_libxml2_version (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ libxml2_version()
/
/ return a text string representing the current LIBXML2 version
/ or NULL if LIBXML2 is currently unsupported
*/

#ifdef ENABLE_LIBXML2		/* LIBXML2 version */
    int len;
    const char *p_result = gaia_libxml2_version ();
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    len = strlen (p_result);
    sqlite3_result_text (context, p_result, len, free);
#else
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_null (context);
#endif
}

static void
fnct_has_rttopo (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasRtTopo()
/
/ return 1 if built including RTTOPO; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef ENABLE_RTTOPO		/* RTTOPO is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_iconv (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasIconv()
/
/ return 1 if built including ICONV; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_ICONV		/* ICONV is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_math_sql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasMathSql()
/
/ return 1 if built including MATHSQL; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_MATHSQL		/* MATHSQL is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geo_callbacks (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ HasGeoCallbacks()
/
/ return 1 if built enabling GEOCALLBACKS; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_GEOCALLBACKS	/* GEO-CALLBACKS are supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_freeXL (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasFreeXL()
/
/ return 1 if built including FreeXL; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_FREEXL		/* FreeXL is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_epsg (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasEpsg()
/
/ return 1 if built including EPSG; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_EPSG		/* EPSG is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_libxml2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasLibXML2()
/
/ return 1 if built including LIBXML2; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef ENABLE_LIBXML2		/* LIBXML2 is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_geopackage (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasGeoPackage()
/
/ return 1 if built including GeoPackage support (GPKG); otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_gcp (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasGCP()
/ HasGroundControlPoints()
/
/ return 1 if built including GroundControlPoints support (GGP); otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef ENABLE_GCP		/* GCP are supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_topology (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasTopology()
/
/ return 1 if built including GroundControlPoints support (GGP); otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifdef ENABLE_RTTOPO		/* RTTOPO is supported */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_knn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasKNN()
/
/ return 1 if built including KNN support; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_GEOS		/* only if GEOS is enabled */
#ifndef OMIT_KNN		/* only if KNN is enabled */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
#else
    sqlite3_result_int (context, 0);
#endif
}

static void
fnct_has_routing (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ HasRouting()
/
/ return 1 if built including VirtualRouting support; otherwise 0
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
#ifndef OMIT_GEOS		/* only if GEOS is enabled */
    sqlite3_result_int (context, 1);
#else
    sqlite3_result_int (context, 0);
#endif
}


static void
fnct_GeometryConstraints (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ GeometryConstraints(BLOBencoded geometry, geometry-type, srid)
/ GeometryConstraints(BLOBencoded geometry, geometry-type, srid, dimensions)
/
/ checks geometry constraints, returning:
/
/ -1 - if some error occurred
/ 1 - if geometry constraints validation passes
/ 0 - if geometry constraints validation fails
/
*/
    int little_endian;
    int endian_arch = gaiaEndianArch ();
    unsigned char *p_blob = NULL;
    int n_bytes = 0;
    int srid;
    int geom_srid = -1;
    const char *type;
    int xtype;
    int geom_type = -1;
    int geom_normalized_type;
    const unsigned char *dimensions;
    int dims = GAIA_XY;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB
	|| sqlite3_value_type (argv[0]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	type = (const char *) sqlite3_value_text (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  /* current metadata style >= v.4.0.0 */
	  type = "UNKNOWN";
	  switch (sqlite3_value_int (argv[1]))
	    {
	    case 0:
		type = "GEOMETRY";
		dims = GAIA_XY;
		break;
	    case 1:
		type = "POINT";
		dims = GAIA_XY;
		break;
	    case 2:
		type = "LINESTRING";
		dims = GAIA_XY;
		break;
	    case 3:
		type = "POLYGON";
		dims = GAIA_XY;
		break;
	    case 4:
		type = "MULTIPOINT";
		dims = GAIA_XY;
		break;
	    case 5:
		type = "MULTILINESTRING";
		dims = GAIA_XY;
		break;
	    case 6:
		type = "MULTIPOLYGON";
		dims = GAIA_XY;
		break;
	    case 7:
		type = "GEOMETRYCOLLECTION";
		dims = GAIA_XY;
		break;
	    case 1000:
		type = "GEOMETRY";
		dims = GAIA_XY_Z;
		break;
	    case 1001:
		type = "POINT";
		dims = GAIA_XY_Z;
		break;
	    case 1002:
		type = "LINESTRING";
		dims = GAIA_XY_Z;
		break;
	    case 1003:
		type = "POLYGON";
		dims = GAIA_XY_Z;
		break;
	    case 1004:
		type = "MULTIPOINT";
		dims = GAIA_XY_Z;
		break;
	    case 1005:
		type = "MULTILINESTRING";
		dims = GAIA_XY_Z;
		break;
	    case 1006:
		type = "MULTIPOLYGON";
		dims = GAIA_XY_Z;
		break;
	    case 1007:
		type = "GEOMETRYCOLLECTION";
		dims = GAIA_XY_Z;
		break;
	    case 2000:
		type = "GEOMETRY";
		dims = GAIA_XY_M;
		break;
	    case 2001:
		type = "POINT";
		dims = GAIA_XY_M;
		break;
	    case 2002:
		type = "LINESTRING";
		dims = GAIA_XY_M;
		break;
	    case 2003:
		type = "POLYGON";
		dims = GAIA_XY_M;
		break;
	    case 2004:
		type = "MULTIPOINT";
		dims = GAIA_XY_M;
		break;
	    case 2005:
		type = "MULTILINESTRING";
		dims = GAIA_XY_M;
		break;
	    case 2006:
		type = "MULTIPOLYGON";
		dims = GAIA_XY_M;
		break;
	    case 2007:
		type = "GEOMETRYCOLLECTION";
		dims = GAIA_XY_M;
		break;
	    case 3000:
		type = "GEOMETRY";
		dims = GAIA_XY_Z_M;
		break;
	    case 3001:
		type = "POINT";
		dims = GAIA_XY_Z_M;
		break;
	    case 3002:
		type = "LINESTRING";
		dims = GAIA_XY_Z_M;
		break;
	    case 3003:
		type = "POLYGON";
		dims = GAIA_XY_Z_M;
		break;
	    case 3004:
		type = "MULTIPOINT";
		dims = GAIA_XY_Z_M;
		break;
	    case 3005:
		type = "MULTILINESTRING";
		dims = GAIA_XY_Z_M;
		break;
	    case 3006:
		type = "MULTIPOLYGON";
		dims = GAIA_XY_Z_M;
		break;
	    case 3007:
		type = "GEOMETRYCOLLECTION";
		dims = GAIA_XY_Z_M;
		break;
	    };
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[2]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc == 4)
      {
	  /* explicit dimensions - supporting XYZM */
	  dimensions = sqlite3_value_text (argv[3]);
	  if (strcasecmp ((char *) dimensions, "XYZ") == 0)
	      dims = GAIA_XY_Z;
	  else if (strcasecmp ((char *) dimensions, "XYM") == 0)
	      dims = GAIA_XY_M;
	  else if (strcasecmp ((char *) dimensions, "XYZM") == 0)
	      dims = GAIA_XY_Z_M;
	  else
	      dims = GAIA_XY;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
      }
    if (p_blob)
      {
	  if (n_bytes == 24 || n_bytes == 32 || n_bytes == 40)
	    {
		/* testing for a possible TinyPoint BLOB */
		if (*(p_blob + 0) == GAIA_MARK_START &&
		    (*(p_blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN
		     || *(p_blob + 1) == GAIA_TINYPOINT_BIG_ENDIAN)
		    && *(p_blob + (n_bytes - 1)) == GAIA_MARK_END)
		  {
		      /* quick TinyPoint validation */
		      int pointType;
		      if (*(p_blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN)
			  little_endian = 1;
		      else if (*(p_blob + 1) == GAIA_TINYPOINT_BIG_ENDIAN)
			  little_endian = 0;
		      else
			  goto illegal_geometry;	/* unknown encoding; neither little-endian nor big-endian */
		      geom_srid =
			  gaiaImport32 (p_blob + 2, little_endian, endian_arch);
		      pointType = *(p_blob + 6);
		      switch (pointType)
			{
			case GAIA_TINYPOINT_XY:
			    geom_type = GAIA_POINT;
			    break;
			case GAIA_TINYPOINT_XYZ:
			    geom_type = GAIA_POINTZ;
			    break;
			case GAIA_TINYPOINT_XYM:
			    geom_type = GAIA_POINTM;
			    break;
			case GAIA_TINYPOINT_XYZM:
			    geom_type = GAIA_POINTZM;
			    break;
			default:
			    goto illegal_geometry;
			};
		      goto valid_geometry;
		  }
	    }

	  /* quick Geometry validation */
	  if (n_bytes < 45)
	      goto illegal_geometry;	/* cannot be an internal BLOB WKB geometry */
	  if (*(p_blob + 0) != GAIA_MARK_START)
	      goto illegal_geometry;	/* failed to recognize START signature */
	  if (*(p_blob + (n_bytes - 1)) != GAIA_MARK_END)
	      goto illegal_geometry;	/* failed to recognize END signature */
	  if (*(p_blob + 38) != GAIA_MARK_MBR)
	      goto illegal_geometry;	/* failed to recognize MBR signature */
	  if (*(p_blob + 1) == GAIA_LITTLE_ENDIAN)
	      little_endian = 1;
	  else if (*(p_blob + 1) == GAIA_BIG_ENDIAN)
	      little_endian = 0;
	  else
	      goto illegal_geometry;	/* unknown encoding; neither little-endian nor big-endian */
	  geom_type = gaiaImport32 (p_blob + 39, little_endian, endian_arch);
	  geom_srid = gaiaImport32 (p_blob + 2, little_endian, endian_arch);
	  goto valid_geometry;
	illegal_geometry:
	  sqlite3_result_int (context, -1);
	  return;
      }
  valid_geometry:
    xtype = GAIA_UNKNOWN;
    if (strcasecmp ((char *) type, "POINT") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_POINTZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_POINTM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_POINTZM;
		break;
	    default:
		xtype = GAIA_POINT;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "LINESTRING") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_LINESTRINGZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_LINESTRINGM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_LINESTRINGZM;
		break;
	    default:
		xtype = GAIA_LINESTRING;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "POLYGON") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_POLYGONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_POLYGONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_POLYGONZM;
		break;
	    default:
		xtype = GAIA_POLYGON;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "MULTIPOINT") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTIPOINTZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTIPOINTM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTIPOINTZM;
		break;
	    default:
		xtype = GAIA_MULTIPOINT;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "MULTILINESTRING") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTILINESTRINGZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTILINESTRINGM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTILINESTRINGZM;
		break;
	    default:
		xtype = GAIA_MULTILINESTRING;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGON") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTIPOLYGONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTIPOLYGONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTIPOLYGONZM;
		break;
	    default:
		xtype = GAIA_MULTIPOLYGON;
		break;
	    };
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTION") == 0)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_GEOMETRYCOLLECTIONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_GEOMETRYCOLLECTIONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_GEOMETRYCOLLECTIONZM;
		break;
	    default:
		xtype = GAIA_GEOMETRYCOLLECTION;
		break;
	    };
      }
    switch (geom_type)
      {
	  /* adjusting COMPRESSED Geometries */
      case GAIA_COMPRESSED_LINESTRING:
	  geom_normalized_type = GAIA_LINESTRING;
	  break;
      case GAIA_COMPRESSED_LINESTRINGZ:
	  geom_normalized_type = GAIA_LINESTRINGZ;
	  break;
      case GAIA_COMPRESSED_LINESTRINGM:
	  geom_normalized_type = GAIA_LINESTRINGM;
	  break;
      case GAIA_COMPRESSED_LINESTRINGZM:
	  geom_normalized_type = GAIA_LINESTRINGZM;
	  break;
      case GAIA_COMPRESSED_POLYGON:
	  geom_normalized_type = GAIA_POLYGON;
	  break;
      case GAIA_COMPRESSED_POLYGONZ:
	  geom_normalized_type = GAIA_POLYGONZ;
	  break;
      case GAIA_COMPRESSED_POLYGONM:
	  geom_normalized_type = GAIA_POLYGONM;
	  break;
      case GAIA_COMPRESSED_POLYGONZM:
	  geom_normalized_type = GAIA_POLYGONZM;
	  break;
      default:
	  geom_normalized_type = geom_type;
	  break;
      };
    if (strcasecmp ((char *) type, "GEOMETRY") == 0)
	xtype = -1;
    if (xtype == GAIA_UNKNOWN)
	sqlite3_result_int (context, -1);
    else
      {
	  ret = 1;
	  if (p_blob)
	    {
		/* skipping NULL Geometry; this is assumed to be always good */
		if (geom_srid != srid)
		    ret = 0;
		if (xtype == -1)
		    ;
		else if (xtype != geom_normalized_type)
		    ret = 0;
	    }
	  sqlite3_result_int (context, ret);
      }
}

static void
fnct_RTreeAlign (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ RTreeAlign(RTree-table-name, PKID-value, BLOBencoded geometry)
/
/ attempts to update the associated R*Tree, returning:
/
/ -1 - if some invalid arg was passed
/ 1 - successful update
/ 0 - update failure
/
*/
    unsigned char *p_blob = NULL;
    int n_bytes = 0;
    sqlite3_int64 pkid;
    const char *rtree_table;
    char *table_name;
    int len;
    char pkv[64];
    gaiaGeomCollPtr geom = NULL;
    int ret;
    char *sql_statement;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	rtree_table = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	pkid = sqlite3_value_int64 (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_BLOB
	|| sqlite3_value_type (argv[2]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
      {
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
	  n_bytes = sqlite3_value_bytes (argv[2]);
	  geom = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
      }

    if (geom == NULL)
      {
	  /* NULL geometry: nothing to do */
	  sqlite3_result_int (context, 1);
      }
    else
      {
	  /* INSERTing into the R*Tree */
	  if (*(rtree_table + 0) == '"'
	      && *(rtree_table + strlen (rtree_table) - 1) == '"')
	    {
		/* earlier versions may pass an already quoted name */
		char *dequoted_table_name;
		len = strlen (rtree_table);
		table_name = malloc (len + 1);
		strcpy (table_name, rtree_table);
		dequoted_table_name = gaiaDequotedSql (table_name);
		free (table_name);
		if (dequoted_table_name == NULL)
		  {
		      sqlite3_result_int (context, -1);
		      return;
		  }
		table_name = gaiaDoubleQuotedSql (dequoted_table_name);
		free (dequoted_table_name);
	    }
	  else
	      table_name = gaiaDoubleQuotedSql (rtree_table);
	  sprintf (pkv, FRMT64, pkid);
	  sql_statement =
	      sqlite3_mprintf
	      ("INSERT INTO \"%s\" (pkid, xmin, ymin, xmax, ymax) "
	       "VALUES (%s, %1.12f, %1.12f, %1.12f, %1.12f)", table_name,
	       pkv, geom->MinX, geom->MinY, geom->MaxX, geom->MaxY);
	  gaiaFreeGeomColl (geom);
	  ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
	  sqlite3_free (sql_statement);
	  free (table_name);
	  if (ret != SQLITE_OK)
	      sqlite3_result_int (context, 0);
	  else
	      sqlite3_result_int (context, 1);
      }
}

static void
fnct_IsValidFont (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsValidFont(BLOBencoded font)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return 0 (FALSE)
/ or -1 (INVALID ARGS)
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_CheckFontFacename (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ CheckFontfaceName(TEXT facename, BLOBencoded font)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return NULL
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_GetFontFamily (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GetFontFamily(BLOBencoded font)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return NULL
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_null (context);
}

static void
fnct_IsFontBold (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsFontBold(BLOBencoded font)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return -1
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_int (context, -1);
}

static void
fnct_IsFontItalic (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsFontItalic(BLOBencoded font)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return -1
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_int (context, -1);
}

static void
fnct_IsValidPixel (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsValidPixel(BLOBencoded pixel, text sample_type, int num_bands)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return 0 (FALSE)
/ or -1 (INVALID ARGS)
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_IsValidRasterPalette (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ IsValidRasterPalette(BLOBencoded palette, text sample_type)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return 0 (FALSE)
/ or -1 (INVALID ARGS)
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_IsValidRasterStatistics (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ IsValidRasterStatistics(text db_prefix, text coverage, 
/                         BLOBencoded statistics)
/   or
/ IsValidRasterStatistics(BLOBencoded statistics, text sample_type, 
/                         int num_bands)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return 0 (FALSE)
/ or -1 (INVALID ARGS)
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if ((sqlite3_value_type (argv[0]) == SQLITE_TEXT
	 || sqlite3_value_type (argv[0]) == SQLITE_NULL)
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[2]) == SQLITE_BLOB)
	;
    else if (sqlite3_value_type (argv[0]) == SQLITE_BLOB
	     && sqlite3_value_type (argv[1]) == SQLITE_TEXT
	     && sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	;
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_IsValidRasterTile (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ IsValidRasterTile(text db_prefix, text coverage, integer level, 
/                   BLOBencoded tile_odd, BLOBencoded tile_even)
/
/ basic version intended to be overloaded by RasterLite-2
/ always return 0 (FALSE)
/ or -1 (INVALID ARGS)
/
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[0]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[3]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[4]) != SQLITE_BLOB
	&& sqlite3_value_type (argv[4]) != SQLITE_NULL)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, 0);
}

static void
fnct_IsPopulatedCoverage (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ IsPopulatedCoverage(text db_prefix, text coverage)
/
/ check if a RasterCoverage is already populated 
/ returns 1 if TRUE, 0 if FALSE
/ -1 on invalid arguments
*/
    const char *db_prefix = NULL;
    const char *coverage;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[0]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage = (const char *) sqlite3_value_text (argv[0]);
    ret = checkPopulatedCoverage (sqlite, db_prefix, coverage);
    sqlite3_result_int (context, ret);
    return;
}

static int
is_without_rowid_table (sqlite3 * sqlite, const char *table)
{
/* internal utility functions; checks for WITHOUT ROWID tables */
    char *sql;
    char *xtable;
    int ret;
    int i;
    char **results;
    int rows;
    int columns;
    int j;
    char **results2;
    int rows2;
    int columns2;
    char *errMsg = NULL;
    int without_rowid = 0;

    xtable = gaiaDoubleQuotedSql (table);
    sql = sqlite3_mprintf ("PRAGMA index_list(\"%s\")", xtable);
    free (xtable);
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
    sqlite3_free (sql);
    if (ret != SQLITE_OK)
      {
	  sqlite3_free (errMsg);
	  return 1;
      }
    for (i = 1; i <= rows; i++)
      {
	  const char *index = results[(i * columns) + 1];
	  sql = sqlite3_mprintf ("SELECT count(*) FROM sqlite_master WHERE "
				 "type = 'index' AND Lower(tbl_name) = Lower(%Q) "
				 "AND Lower(name) = Lower(%Q)", table, index);
	  ret =
	      sqlite3_get_table (sqlite, sql, &results2, &rows2, &columns2,
				 &errMsg);
	  sqlite3_free (sql);
	  if (ret != SQLITE_OK)
	    {
		sqlite3_free (errMsg);
		return 1;
	    }
	  for (j = 1; j <= rows2; j++)
	    {
		if (atoi (results2[(j * columns2) + 0]) == 0)
		    without_rowid = 1;
	    }
	  sqlite3_free_table (results2);
      }
    sqlite3_free_table (results);
    return without_rowid;
}

static int
checkDatabase (const sqlite3 * handle, const char *db_prefix)
{
/* testing if some ATTACHED-DB do really exist */
    sqlite3 *sqlite = (sqlite3 *) handle;
    char *xdb_prefix;
    char sql[1024];
    int ret;
    int i;
    char **results;
    int rows;
    int columns;
    int exists = 0;

    if (db_prefix == NULL)
	db_prefix = "main";
    xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
    sprintf (sql, "PRAGMA \"%s\".database_list", xdb_prefix);
    free (xdb_prefix);
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	return 0;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		const char *name = results[(i * columns) + 1];
		if (strcasecmp (name, db_prefix) == 0)
		    exists = 1;
	    }
      }
    sqlite3_free_table (results);
    return exists;
}

static int
checkGeoPackage (sqlite3 * handle, const char *db_prefix)
{
/* testing for GeoPackage meta-tables */
    sqlite3 *sqlite = (sqlite3 *) handle;
    char *xdb_prefix;
    char sql[1024];
    int ret;
    const char *name;
    int table_name = 0;
    int column_name = 0;
    int geometry_type_name = 0;
    int srs_id_gc = 0;
    int has_z = 0;
    int has_m = 0;
    int gpkg_gc = 0;
    int srs_id_srs = 0;
    int srs_name = 0;
    int gpkg_srs = 0;
    int i;
    char **results;
    int rows;
    int columns;

    if (!checkDatabase (handle, db_prefix))
	return -1;
/* checking the GPKG_GEOMETRY_COLUMNS table */
    if (db_prefix == NULL)
	db_prefix = "main";
    xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
    sprintf (sql, "PRAGMA \"%s\".table_info(gpkg_geometry_columns)",
	     xdb_prefix);
    free (xdb_prefix);
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	goto unknown;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		name = results[(i * columns) + 1];
		if (strcasecmp (name, "table_name") == 0)
		    table_name = 1;
		if (strcasecmp (name, "column_name") == 0)
		    column_name = 1;
		if (strcasecmp (name, "geometry_type_name") == 0)
		    geometry_type_name = 1;
		if (strcasecmp (name, "srs_id") == 0)
		    srs_id_gc = 1;
		if (strcasecmp (name, "z") == 0)
		    has_z = 1;
		if (strcasecmp (name, "m") == 0)
		    has_m = 1;
	    }
      }
    sqlite3_free_table (results);
    if (table_name && column_name && geometry_type_name && srs_id_gc && has_z
	&& has_m)
	gpkg_gc = 1;
/* checking the GPKG_SPATIAL_REF_SYS table */
    strcpy (sql, "PRAGMA table_info(gpkg_spatial_ref_sys)");
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	goto unknown;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		name = results[(i * columns) + 1];
		if (strcasecmp (name, "srs_id") == 0)
		    srs_id_srs = 1;
		if (strcasecmp (name, "srs_name") == 0)
		    srs_name = 1;
	    }
      }
    sqlite3_free_table (results);
    if (srs_id_srs && srs_name)
	gpkg_srs = 1;
    if (gpkg_gc && gpkg_srs)
	return 1;
  unknown:
    return 0;
}

SPATIALITE_PRIVATE int
checkSpatialMetaData (const void *handle)
{
/* just calls checkSpatialMetaData_ex */
    return checkSpatialMetaData_ex (handle, NULL);
}

SPATIALITE_PRIVATE int
checkSpatialMetaData_ex (const void *handle, const char *db_prefix)
{
/* internal utility function:
/
/ for FDO-OGR interoperability and cross-version seamless compatibility:
/ tests the SpatialMetadata type, returning:
/
/ -1 -  if no ATTACHED-DB identified by db_prefix exists
/ 0 - if no valid SpatialMetaData were found
/ 1 - if SpatiaLite-like (legacy) SpatialMetadata were found
/ 2 - if FDO-OGR-like SpatialMetadata were found
/ 3 - if SpatiaLite-like (current) SpatialMetadata were 
/ 4 - if GeoPackage SpatialMetadata were found
/
*/
    sqlite3 *sqlite = (sqlite3 *) handle;
    char *xdb_prefix;
    int spatialite_legacy_rs = 0;
    int spatialite_rs = 0;
    int fdo_rs = 0;
    int spatialite_legacy_gc = 0;
    int spatialite_gc = 0;
    int fdo_gc = 0;
    int rs_srid = 0;
    int auth_name = 0;
    int auth_srid = 0;
    int srtext = 0;
    int ref_sys_name = 0;
    int proj4text = 0;
    int f_table_name = 0;
    int f_geometry_column = 0;
    int geometry_type = 0;
    int coord_dimension = 0;
    int gc_srid = 0;
    int geometry_format = 0;
    int type = 0;
    int spatial_index_enabled = 0;
    char sql[1024];
    int ret;
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;

    if (!checkDatabase (handle, db_prefix))
	return -1;
/* checking the GEOMETRY_COLUMNS table */
    if (db_prefix == NULL)
	db_prefix = "main";
    xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
    sprintf (sql, "PRAGMA \"%s\".table_info(geometry_columns)", xdb_prefix);
    free (xdb_prefix);
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	goto unknown;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		name = results[(i * columns) + 1];
		if (strcasecmp (name, "f_table_name") == 0)
		    f_table_name = 1;
		if (strcasecmp (name, "f_geometry_column") == 0)
		    f_geometry_column = 1;
		if (strcasecmp (name, "geometry_type") == 0)
		    geometry_type = 1;
		if (strcasecmp (name, "coord_dimension") == 0)
		    coord_dimension = 1;
		if (strcasecmp (name, "srid") == 0)
		    gc_srid = 1;
		if (strcasecmp (name, "geometry_format") == 0)
		    geometry_format = 1;
		if (strcasecmp (name, "type") == 0)
		    type = 1;
		if (strcasecmp (name, "spatial_index_enabled") == 0)
		    spatial_index_enabled = 1;
	    }
      }
    sqlite3_free_table (results);
    if (f_table_name && f_geometry_column && type && coord_dimension
	&& gc_srid && spatial_index_enabled)
	spatialite_legacy_gc = 1;
    if (f_table_name && f_geometry_column && geometry_type && coord_dimension
	&& gc_srid && spatial_index_enabled)
	spatialite_gc = 1;
    if (f_table_name && f_geometry_column && geometry_type && coord_dimension
	&& gc_srid && geometry_format)
	fdo_gc = 1;
/* checking the SPATIAL_REF_SYS table */
    strcpy (sql, "PRAGMA table_info(spatial_ref_sys)");
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	goto unknown;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		name = results[(i * columns) + 1];
		if (strcasecmp (name, "srid") == 0)
		    rs_srid = 1;
		if (strcasecmp (name, "auth_name") == 0)
		    auth_name = 1;
		if (strcasecmp (name, "auth_srid") == 0)
		    auth_srid = 1;
		if (strcasecmp (name, "srtext") == 0)
		    srtext = 1;
		if (strcasecmp (name, "ref_sys_name") == 0)
		    ref_sys_name = 1;
		if (strcasecmp (name, "proj4text") == 0)
		    proj4text = 1;
		if (strcasecmp (name, "srtext") == 0)
		    srtext = 1;
	    }
      }
    sqlite3_free_table (results);
    if (rs_srid && auth_name && auth_srid && ref_sys_name && proj4text
	&& srtext)
	spatialite_rs = 1;
    if (rs_srid && auth_name && auth_srid && ref_sys_name && proj4text)
	spatialite_legacy_rs = 1;
    if (rs_srid && auth_name && auth_srid && srtext)
	fdo_rs = 1;
/* verifying the MetaData format */
    if (spatialite_legacy_gc && spatialite_legacy_rs)
	return 1;
    if (fdo_gc && fdo_rs)
	return 2;
    if (spatialite_gc && spatialite_rs)
	return 3;
  unknown:
    if (checkGeoPackage (sqlite, db_prefix))
	return 4;
    return 0;
}

static void
add_fdo_table (struct fdo_table **first, struct fdo_table **last,
	       const char *table, int len)
{
/* adds an FDO-OGR styled Geometry Table to corresponding linked list */
    struct fdo_table *p = malloc (sizeof (struct fdo_table));
    p->table = malloc (len + 1);
    strcpy (p->table, table);
    p->next = NULL;
    if (!(*first))
	(*first) = p;
    if ((*last))
	(*last)->next = p;
    (*last) = p;
}

static void
free_fdo_tables (struct fdo_table *first)
{
/* memory cleanup; destroying the FDO-OGR tables linked list */
    struct fdo_table *p;
    struct fdo_table *pn;
    p = first;
    while (p)
      {
	  pn = p->next;
	  if (p->table)
	      free (p->table);
	  free (p);
	  p = pn;
      }
}

static void
fnct_AutoFDOStart (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AutoFDOStart(void)
/     or
/ AutoFDOStart(db_prefix TEXT)
/
/ for FDO-OGR interoperability:
/ tests the SpatialMetadata type, then automatically
/ creating a VirtualFDO table for each FDO-OGR main table 
/ declared within FDO-styled SpatialMetadata
/
*/
    int ret;
    const char *db_prefix = "main";
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;
    char *sql_statement;
    int count = 0;
    struct fdo_table *first = NULL;
    struct fdo_table *last = NULL;
    struct fdo_table *p;
    int len;
    char *xname;
    char *xxname;
    char *xtable;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	      goto null_prefix;
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
  null_prefix:
    if (checkSpatialMetaData_ex (sqlite, db_prefix) == 2)
      {
	  /* ok, creating VirtualFDO tables */
	  char *xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
	  sql_statement =
	      sqlite3_mprintf
	      ("SELECT DISTINCT f_table_name FROM \"%s\".geometry_columns",
	       xdb_prefix);
	  free (xdb_prefix);
	  ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows,
				   &columns, NULL);
	  sqlite3_free (sql_statement);
	  if (ret != SQLITE_OK)
	      goto error;
	  if (rows < 1)
	      ;
	  else
	    {
		for (i = 1; i <= rows; i++)
		  {
		      name = results[(i * columns) + 0];
		      if (name)
			{
			    len = strlen (name);
			    add_fdo_table (&first, &last, name, len);
			}
		  }
	    }
	  sqlite3_free_table (results);
	  p = first;
	  while (p)
	    {
		/* destroying the VirtualFDO table [if existing] */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("fdo_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		sql_statement =
		    sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\".\"%s\"",
				     xdb_prefix, xname);
		free (xname);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		/* creating the VirtualFDO table */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("fdo_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		xtable = gaiaDoubleQuotedSql (p->table);
		sql_statement =
		    sqlite3_mprintf
		    ("CREATE VIRTUAL TABLE \"%s\".\"%s\" USING VirtualFDO(\"%s\", \"%s\")",
		     xdb_prefix, xname, xdb_prefix, xtable);
		free (xname);
		free (xtable);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		count++;
		p = p->next;
	    }
	error:
	  free_fdo_tables (first);
	  sqlite3_result_int (context, count);
	  return;
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_AutoFDOStop (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AutoFDOStop(void)
/     or
/ AutoFDOStop(db_prefix TEXT)
/
/ for FDO-OGR interoperability:
/ tests the SpatialMetadata type, then automatically
/ removes any VirtualFDO table 
/
*/
    int ret;
    const char *db_prefix = "main";
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;
    char *sql_statement;
    int count = 0;
    struct fdo_table *first = NULL;
    struct fdo_table *last = NULL;
    struct fdo_table *p;
    int len;
    char *xname;
    char *xxname;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	      goto null_prefix;
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
  null_prefix:
    if (checkSpatialMetaData_ex (sqlite, db_prefix) == 2)
      {
	  /* ok, removing VirtualFDO tables */
	  char *xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
	  sql_statement =
	      sqlite3_mprintf
	      ("SELECT DISTINCT f_table_name FROM \"%s\".geometry_columns",
	       xdb_prefix);
	  free (xdb_prefix);
	  ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows,
				   &columns, NULL);
	  if (ret != SQLITE_OK)
	      goto error;
	  if (rows < 1)
	      ;
	  else
	    {
		for (i = 1; i <= rows; i++)
		  {
		      name = results[(i * columns) + 0];
		      if (name)
			{
			    len = strlen (name);
			    add_fdo_table (&first, &last, name, len);
			}
		  }
	    }
	  sqlite3_free_table (results);
	  p = first;
	  while (p)
	    {
		/* destroying the VirtualFDO table [if existing] */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("fdo_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		sql_statement =
		    sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\".\"%s\"",
				     xdb_prefix, xname);
		free (xname);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		count++;
		p = p->next;
	    }
	error:
	  free_fdo_tables (first);
	  sqlite3_result_int (context, count);
	  return;
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CheckSpatialMetaData (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ CheckSpatialMetaData(void)
/     or
/ CheckSpatialMetaData(db_prefix TEXT)
/
/ for FDO-OGR interoperability:
/ tests the SpatialMetadata type, returning:
/
/ -1 - on invalid args or if no ATTACHED-DB idenfied by db_prefix exists
/ 0 - if no valid SpatialMetaData were found
/ 1 - if SpatiaLite-legacy SpatialMetadata were found
/ 2 - if FDO-OGR-like SpatialMetadata were found
/ 3 - if SpatiaLite-current SpatialMetadata were found
/ 4 - if GeoPackage SpatialMetadata were found
/
*/
    const char *db_prefix = NULL;
    sqlite3 *sqlite;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
    sqlite = sqlite3_context_db_handle (context);
    ret = checkSpatialMetaData_ex (sqlite, db_prefix);
    if (ret == 3)
      {
	  /* trying to create the advanced metadata tables >= v.4.0.0 */
	  if (db_prefix == NULL || strcasecmp (db_prefix, "main") == 0)
	      createAdvancedMetaData (sqlite);
      }
    sqlite3_result_int (context, ret);
    return;
}

static void
fnct_InitSpatialMetaData (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ InitSpatialMetaData()
/     or
/ InitSpatialMetaData(text mode)
/     or
/ InitSpatialMetaData(integer transaction)
/     or
/ InitSpatialMetaData(integer transaction, text mode)
/
/ creates the SPATIAL_REF_SYS and GEOMETRY_COLUMNS tables
/ returns 1 on success
/ 0 on failure
*/
    char sql[8192];
    char *errMsg = NULL;
    int ret;
    int transaction = 0;
    const char *xmode;
    int mode = GAIA_EPSG_ANY;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	    {
		xmode = (const char *) sqlite3_value_text (argv[0]);
		if (strcasecmp (xmode, "NONE") == 0
		    || strcasecmp (xmode, "EMPTY") == 0)
		    mode = GAIA_EPSG_NONE;
		if (strcasecmp (xmode, "WGS84") == 0
		    || strcasecmp (xmode, "WGS84_ONLY") == 0)
		    mode = GAIA_EPSG_WGS84_ONLY;
	    }
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	      transaction = sqlite3_value_int (argv[0]);
	  else
	    {
		spatialite_e
		    ("InitSpatialMetaData() error: argument 1 is not of the String or Integer type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
	    {
		spatialite_e
		    ("InitSpatialMetaData() error: argument 1 is not of the Integer type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("InitSpatialMetaData() error: argument 2 is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  transaction = sqlite3_value_int (argv[0]);
	  xmode = (const char *) sqlite3_value_text (argv[1]);
	  if (strcasecmp (xmode, "NONE") == 0
	      || strcasecmp (xmode, "EMPTY") == 0)
	      mode = GAIA_EPSG_NONE;
	  if (strcasecmp (xmode, "WGS84") == 0
	      || strcasecmp (xmode, "WGS84_ONLY") == 0)
	      mode = GAIA_EPSG_WGS84_ONLY;
      }

    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

/* creating the SPATIAL_REF_SYS table */
    strcpy (sql, "CREATE TABLE spatial_ref_sys (\n");
    strcat (sql, "srid INTEGER NOT NULL PRIMARY KEY,\n");
    strcat (sql, "auth_name TEXT NOT NULL,\n");
    strcat (sql, "auth_srid INTEGER NOT NULL,\n");
    strcat (sql, "ref_sys_name TEXT NOT NULL DEFAULT 'Unknown',\n");
    strcat (sql, "proj4text TEXT NOT NULL,\n");
    strcat (sql, "srtext TEXT NOT NULL DEFAULT 'Undefined')");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
    strcpy (sql, "CREATE UNIQUE INDEX idx_spatial_ref_sys \n");
    strcat (sql, "ON spatial_ref_sys (auth_srid, auth_name)");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
    updateSpatiaLiteHistory (sqlite, "spatial_ref_sys", NULL,
			     "table successfully created");

/* creating the GEOMETRY_COLUMNS table */
    if (!createGeometryColumns (sqlite))
	goto error;

/* creating the GEOM_COLS_REF_SYS view */
    strcpy (sql, "CREATE VIEW geom_cols_ref_sys AS\n");
    strcat (sql, "SELECT f_table_name, f_geometry_column, geometry_type,\n");
    strcat (sql, "coord_dimension, spatial_ref_sys.srid AS srid,\n");
    strcat (sql, "auth_name, auth_srid, ref_sys_name, proj4text, srtext\n");
    strcat (sql, "FROM geometry_columns, spatial_ref_sys\n");
    strcat (sql, "WHERE geometry_columns.srid = spatial_ref_sys.srid");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    updateSpatiaLiteHistory (sqlite, "geom_cols_ref_sys", NULL,
			     "view 'geom_cols_ref_sys' successfully created");
    if (ret != SQLITE_OK)
	goto error;
    if (spatial_ref_sys_init2 (sqlite, mode, 0))
      {
	  if (mode == GAIA_EPSG_NONE)
	      updateSpatiaLiteHistory (sqlite, "spatial_ref_sys", NULL,
				       "table successfully created [empty]");
	  else
	      updateSpatiaLiteHistory (sqlite, "spatial_ref_sys", NULL,
				       "table successfully populated");
      }
    if (!createAdvancedMetaData (sqlite))
	goto error;
/* creating the SpatialIndex VIRTUAL TABLE */
    strcpy (sql, "CREATE VIRTUAL TABLE SpatialIndex ");
    strcat (sql, "USING VirtualSpatialIndex()");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
/* creating the ElementaryGeometries VIRTUAL TABLE */
    strcpy (sql, "CREATE VIRTUAL TABLE ElementaryGeometries ");
    strcat (sql, "USING VirtualElementary()");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;

#ifndef OMIT_KNN		/* only if KNN is enabled */
/* creating the KNN VIRTUAL TABLE */
    strcpy (sql, "CREATE VIRTUAL TABLE KNN ");
    strcat (sql, "USING VirtualKNN()");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
#endif /* end KNN conditional */

    if (transaction)
      {
	  /* confirming the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("InitSpatiaMetaData() error:\"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    if (transaction)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e (" InitSpatiaMetaData() error:\"%s\"\n", errMsg);
		sqlite3_free (errMsg);
	    }
      }
    sqlite3_result_int (context, 0);
    return;
}

static int
do_execute_sql_with_retval (sqlite3 * sqlite, const char *sql, char **errMsg)
{
/* helper function for InitSpatialMetaDataFull */
    int retval = 0;
    int ret;
    int i;
    char **results;
    int rows;
    int columns;
    char *msg = NULL;

    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &msg);
    if (ret != SQLITE_OK)
	goto end;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		if (atoi (results[(i * columns) + 0]) == 1)
		    retval = 1;
	    }
      }
    sqlite3_free_table (results);

  end:
    *errMsg = msg;
    return retval;
}

static void
fnct_InitSpatialMetaDataFull (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ InitSpatialMetaDataFull()
/     or
/ InitSpatialMetaDataFull(text mode)
/     or
/ InitSpatialMetaDataFull(integer transaction)
/     or
/ InitSpatialMetaDataFull(integer transaction, text mode)
/
/ conveniency "super" function internally calling in a single shot:
/     - InitSpatialMetaData()
/     - CreateRasterCoveragesTable()
/     - CreateVectorCoveragesTables()
/     - CreateStylingTables()
/ returns 1 on success
/ 0 on failure
*/
    char *errMsg = NULL;
    int ret;
    int transaction = 0;
    const char *xmode = NULL;
    int retval;
    char *sql;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	    {
		xmode = (const char *) sqlite3_value_text (argv[0]);
	    }
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	      transaction = sqlite3_value_int (argv[0]);
	  else
	    {
		spatialite_e
		    ("InitSpatialMetaDataFull() error: argument 1 is not of the String or Integer type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
	    {
		spatialite_e
		    ("InitSpatialMetaDataFull() error: argument 1 is not of the Integer type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("InitSpatialMetaDataFull() error: argument 2 is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  transaction = sqlite3_value_int (argv[0]);
	  xmode = (const char *) sqlite3_value_text (argv[1]);
      }

    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

/* executing InitSpatialMetaData() */
    if (xmode != NULL)
	sql = sqlite3_mprintf ("SELECT InitSpatialMetaData(%Q)", xmode);
    else
	sql = sqlite3_mprintf ("SELECT InitSpatialMetaData()");
    retval = do_execute_sql_with_retval (sqlite, sql, &errMsg);
    sqlite3_free (sql);
    if (retval != 1)
	goto error;

/* executing CreateRasterCoveragesTable() */
    sql = sqlite3_mprintf ("SELECT CreateRasterCoveragesTable()");
    retval = do_execute_sql_with_retval (sqlite, sql, &errMsg);
    sqlite3_free (sql);
    if (retval != 1)
	goto error;

/* executing CreateVectorCoveragesTables() */
    sql = sqlite3_mprintf ("SELECT CreateVectorCoveragesTables()");
    retval = do_execute_sql_with_retval (sqlite, sql, &errMsg);
    sqlite3_free (sql);
    if (retval != 1)
	goto error;

/* executing CreateStylingTables() */
    sql = sqlite3_mprintf ("SELECT CreateStylingTables()");
    retval = do_execute_sql_with_retval (sqlite, sql, &errMsg);
    sqlite3_free (sql);
    if (retval != 1)
	goto error;

/* executing StoredProc_CreateTables() */
    sql = sqlite3_mprintf ("SELECT StoredProc_CreateTables()");
    retval = do_execute_sql_with_retval (sqlite, sql, &errMsg);
    sqlite3_free (sql);
    if (retval != 1)
	goto error;

    if (transaction)
      {
	  /* confirming the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("InitSpatiaMetaDataFull() error:\"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    if (transaction)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e (" InitSpatiaMetaDataFull() error:\"%s\"\n",
			      errMsg);
		sqlite3_free (errMsg);
	    }
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CloneTable (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CloneTable(text db_prefix, text in_table, text out_table, integer transaction)
/ CloneTable(text db_prefix, text in_table, text out_table, integer transaction,
/            ... text option1 ..., ... text option2 ..., text option10)
/
/ cloning a whole table [CREATE and then COPY]
/ returns 1 on success
/ 0 on failure (NULL on invalid arguments)
*/
    int ret;
    char *errMsg = NULL;
    const char *db_prefix;
    const char *in_table;
    const char *out_table;
    int transaction = 0;
    int active = 0;
    const void *cloner = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	db_prefix = "MAIN";
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	db_prefix = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  spatialite_e
	      ("CloneTable() error: argument 1 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	in_table = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  spatialite_e
	      ("CloneTable() error: argument 2 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	out_table = (const char *) sqlite3_value_text (argv[2]);
    else
      {
	  spatialite_e
	      ("CloneTable() error: argument 3 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	transaction = sqlite3_value_int (argv[3]);
    else
      {
	  spatialite_e
	      ("CloneTable() error: argument 4 is not of the Integer type\n");
	  sqlite3_result_null (context);
	  return;
      }

/* additional options */
    if (argc > 4 && sqlite3_value_type (argv[4]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 5 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 5 && sqlite3_value_type (argv[5]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 6 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 6 && sqlite3_value_type (argv[6]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 7 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 7 && sqlite3_value_type (argv[7]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 8 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 8 && sqlite3_value_type (argv[8]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 9 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 9 && sqlite3_value_type (argv[9]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 10 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 10 && sqlite3_value_type (argv[10]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 11 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 11 && sqlite3_value_type (argv[11]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 12 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 12 && sqlite3_value_type (argv[12]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 13 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 13 && sqlite3_value_type (argv[13]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CloneTable() error: argument 14 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }

    cloner = gaiaAuxClonerCreate (sqlite, db_prefix, in_table, out_table);
    if (cloner == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* additional options */
    if (argc > 4)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[4]));
    if (argc > 5)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[5]));
    if (argc > 6)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[6]));
    if (argc > 7)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[7]));
    if (argc > 8)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[8]));
    if (argc > 9)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[9]));
    if (argc > 10)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[10]));
    if (argc > 11)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[11]));
    if (argc > 12)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[12]));
    if (argc > 13)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[13]));

    if (!gaiaAuxClonerCheckValidTarget (cloner))
	goto error;

    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    active = 1;

    if (!gaiaAuxClonerExecute (cloner))
	goto error;
    gaiaAuxClonerDestroy (cloner);
    updateSpatiaLiteHistory (sqlite, out_table, NULL,
			     "table successfully cloned");

    if (transaction)
      {
	  /* confirming the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

    sqlite3_result_int (context, 1);
    return;
  error:
    if (cloner != NULL)
	gaiaAuxClonerDestroy (cloner);
    spatialite_e ("CloneTable() error:\"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    if (transaction && active)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("CloneTable() error:\"%s\"\n", errMsg);
		sqlite3_free (errMsg);
	    }
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CreateClonedTable (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ CreateClonedTable(text db_prefix, text in_table, text out_table, integer transaction)
/ CreateClonedTable(text db_prefix, text in_table, text out_table, integer transaction,
/            ... text option1 ..., ... text option2 ..., text option10)
/
/ creating a cloned table [CREATE only without COPYing]
/ returns 1 on success
/ 0 on failure (NULL on invalid arguments)
*/
    int ret;
    char *errMsg = NULL;
    const char *db_prefix;
    const char *in_table;
    const char *out_table;
    int transaction = 0;
    int active = 0;
    const void *cloner = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	db_prefix = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 1 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	in_table = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 2 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	out_table = (const char *) sqlite3_value_text (argv[2]);
    else
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 3 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	transaction = sqlite3_value_int (argv[3]);
    else
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 4 is not of the Integer type\n");
	  sqlite3_result_null (context);
	  return;
      }


/* additional options */
    if (argc > 4 && sqlite3_value_type (argv[4]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 5 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 5 && sqlite3_value_type (argv[5]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 6 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 6 && sqlite3_value_type (argv[6]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 7 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 7 && sqlite3_value_type (argv[7]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 8 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 8 && sqlite3_value_type (argv[8]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 9 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 9 && sqlite3_value_type (argv[9]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 10 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 10 && sqlite3_value_type (argv[10]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 11 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 11 && sqlite3_value_type (argv[11]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 12 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 12 && sqlite3_value_type (argv[12]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 13 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 13 && sqlite3_value_type (argv[13]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateClonedTable() error: argument 14 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }

    cloner = gaiaAuxClonerCreateEx (sqlite, db_prefix, in_table, out_table, 1);
    if (cloner == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* additional options */
    if (argc > 4)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[4]));
    if (argc > 5)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[5]));
    if (argc > 6)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[6]));
    if (argc > 7)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[7]));
    if (argc > 8)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[8]));
    if (argc > 9)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[9]));
    if (argc > 10)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[10]));
    if (argc > 11)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[11]));
    if (argc > 12)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[12]));
    if (argc > 13)
	gaiaAuxClonerAddOption (cloner,
				(const char *) sqlite3_value_text (argv[13]));

    if (!gaiaAuxClonerCheckValidTarget (cloner))
	goto error;

    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    active = 1;

    if (!gaiaAuxClonerExecute (cloner))
	goto error;
    gaiaAuxClonerDestroy (cloner);
    updateSpatiaLiteHistory (sqlite, out_table, NULL,
			     "table successfully cloned");

    if (transaction)
      {
	  /* confirming the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }

    sqlite3_result_int (context, 1);
    return;
  error:
    if (cloner != NULL)
	gaiaAuxClonerDestroy (cloner);
    spatialite_e ("CreateClonedTable() error:\"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    if (transaction && active)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("CreateClonedTable() error:\"%s\"\n", errMsg);
		sqlite3_free (errMsg);
	    }
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CheckGeoPackageMetaData (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ CheckGeoPackageMetaData(void)
/     or
/ CheckGeoPackageMetaData(db_prefix TEXT)
/
/ for OGC GeoPackage interoperability:
/ tests if GeoPackage metadata tables are found
/
*/
    const char *db_prefix = NULL;
    sqlite3 *sqlite;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
    sqlite = sqlite3_context_db_handle (context);
    ret = checkGeoPackage (sqlite, db_prefix);
    sqlite3_result_int (context, ret);
    return;
}

#ifdef ENABLE_GEOPACKAGE	/* enabling GeoPackage extensions */

static void
add_gpkg_table (struct gpkg_table **first, struct gpkg_table **last,
		const char *table, int len)
{
/* adds a GPKG Geometry Table to corresponding linked list */
    struct gpkg_table *p = malloc (sizeof (struct gpkg_table));
    p->table = malloc (len + 1);
    strcpy (p->table, table);
    p->next = NULL;
    if (!(*first))
	(*first) = p;
    if ((*last))
	(*last)->next = p;
    (*last) = p;
}

static void
free_gpkg_tables (struct gpkg_table *first)
{
/* memory cleanup; destroying the GPKG tables linked list */
    struct gpkg_table *p;
    struct gpkg_table *pn;
    p = first;
    while (p)
      {
	  pn = p->next;
	  if (p->table)
	      free (p->table);
	  free (p);
	  p = pn;
      }
}

static void
fnct_AutoGPKGStart (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AutoGPKGStart(void)
/     or
/ AutoGPKGStart(db_prefix TEXT)
/
/ for OCG GeoPackage interoperability:
/ tests the DB layout, then automatically
/ creating a VirtualGPGK table for each GPKG main table 
/ declared within gpkg_geometry_colums
/
*/
    int ret;
    const char *db_prefix = "main";
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;
    char *sql_statement;
    int count = 0;
    struct gpkg_table *first = NULL;
    struct gpkg_table *last = NULL;
    struct gpkg_table *p;
    int len;
    char *xname;
    char *xxname;
    char *xtable;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	      goto null_prefix;
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
  null_prefix:
    if (checkGeoPackage (sqlite, db_prefix))
      {
	  /* ok, creating VirtualGPKG tables */
	  char *xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
	  sql_statement =
	      sqlite3_mprintf
	      ("SELECT DISTINCT table_name FROM \"%s\".gpkg_geometry_columns",
	       xdb_prefix);
	  free (xdb_prefix);
	  ret =
	      sqlite3_get_table (sqlite, sql_statement, &results, &rows,
				 &columns, NULL);
	  sqlite3_free (sql_statement);
	  if (ret != SQLITE_OK)
	      goto error;
	  if (rows < 1)
	      ;
	  else
	    {
		for (i = 1; i <= rows; i++)
		  {
		      name = results[(i * columns) + 0];
		      if (name)
			{
			    len = strlen (name);
			    add_gpkg_table (&first, &last, name, len);
			}
		  }
	    }
	  sqlite3_free_table (results);
	  p = first;
	  while (p)
	    {
		/* destroying the VirtualGPKG table [if existing] */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("vgpkg_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		sql_statement =
		    sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\".\"%s\"",
				     xdb_prefix, xname);
		free (xname);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		/* creating the VirtualGPKG table */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("vgpkg_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		xtable = gaiaDoubleQuotedSql (p->table);
		sql_statement =
		    sqlite3_mprintf
		    ("CREATE VIRTUAL TABLE \"%s\".\"%s\" USING VirtualGPKG(\"%s\", \"%s\")",
		     xdb_prefix, xname, xdb_prefix, xtable);
		free (xname);
		free (xtable);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		count++;
		p = p->next;
	    }
	error:
	  free_gpkg_tables (first);
	  sqlite3_result_int (context, count);
	  return;
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_AutoGPKGStop (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AutoGPKGStop(void)
/     or
/ AutoGPKGStop(db_prefix TEXT)
/
/ for OGC GeoPackage interoperability:
/ tests the DB layout, then automatically removes any VirtualGPKG table 
/
*/
    int ret;
    const char *db_prefix = "main";
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;
    char *sql_statement;
    int count = 0;
    struct gpkg_table *first = NULL;
    struct gpkg_table *last = NULL;
    struct gpkg_table *p;
    int len;
    char *xname;
    char *xxname;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	      goto null_prefix;
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  db_prefix = (const char *) sqlite3_value_text (argv[0]);
      }
  null_prefix:
    if (checkGeoPackage (sqlite, db_prefix))
      {
	  /* ok, removing VirtualGPKG tables */
	  char *xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
	  sql_statement =
	      sqlite3_mprintf
	      ("SELECT DISTINCT table_name FROM \"%s\".gpkg_geometry_columns",
	       xdb_prefix);
	  free (xdb_prefix);
	  ret =
	      sqlite3_get_table (sqlite, sql_statement, &results, &rows,
				 &columns, NULL);
	  sqlite3_free (sql_statement);
	  if (ret != SQLITE_OK)
	      goto error;
	  if (rows < 1)
	      ;
	  else
	    {
		for (i = 1; i <= rows; i++)
		  {
		      name = results[(i * columns) + 0];
		      if (name)
			{
			    len = strlen (name);
			    add_gpkg_table (&first, &last, name, len);
			}
		  }
	    }
	  sqlite3_free_table (results);
	  p = first;
	  while (p)
	    {
		/* destroying the VirtualGPKG table [if existing] */
		xdb_prefix = gaiaDoubleQuotedSql (db_prefix);
		xxname = sqlite3_mprintf ("vgpkg_%s", p->table);
		xname = gaiaDoubleQuotedSql (xxname);
		sqlite3_free (xxname);
		sql_statement =
		    sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\".\"%s\"",
				     xdb_prefix, xname);
		free (xname);
		free (xdb_prefix);
		ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
		sqlite3_free (sql_statement);
		if (ret != SQLITE_OK)
		    goto error;
		count++;
		p = p->next;
	    }
	error:
	  free_gpkg_tables (first);
	  sqlite3_result_int (context, count);
	  return;
      }
    sqlite3_result_int (context, 0);
    return;
}

#endif /* end enabling GeoPackage extensions */

static void
fnct_InsertEpsgSrid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ InsertEpsgSrid(int srid)
/
/ returns 1 on success: 0 on failure
*/
    int srid;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    ret = insert_epsg_srid (sqlite, srid);
    if (!ret)
	sqlite3_result_int (context, 0);
    else
	sqlite3_result_int (context, 1);
}

static void
fnct_SridIsGeographic (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ SridIsGeographic(int srid)
/
/ returns 1 on success: 0 on failure
/ NULL on invalid argument
*/
    int srid;
    int ret;
    int geographic;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    ret = srid_is_geographic (sqlite, srid, &geographic);
    if (!ret)
	sqlite3_result_null (context);
    else
      {
	  if (geographic)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
}

static void
fnct_SridIsProjected (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ SridIsProjected(int srid)
/
/ returns 1 on success: 0 on failure
/ NULL on invalid argument
*/
    int srid;
    int ret;
    int projected;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    ret = srid_is_projected (sqlite, srid, &projected);
    if (!ret)
	sqlite3_result_null (context);
    else
      {
	  if (projected)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
}

static void
fnct_SridHasFlippedAxes (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ SridHasFlippedAxes(int srid)
/
/ returns 1 on success: 0 on failure
/ NULL on invalid argument
*/
    int srid;
    int ret;
    int flipped;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    ret = srid_has_flipped_axes (sqlite, srid, &flipped);
    if (!ret)
	sqlite3_result_null (context);
    else
      {
	  if (flipped)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
}

static void
fnct_SridGetSpheroid (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ SridGetSpheroid(int srid)
/ or
/ SridGetEllipsoid(int srid)
/
/ returns the name of the Spheroid on success
/ NULL on failure or on invalid argument
*/
    int srid;
    char *spheroid = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    spheroid = srid_get_spheroid (sqlite, srid);
    if (spheroid == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, spheroid, strlen (spheroid), free);
}

static void
fnct_SridGetPrimeMeridian (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ SridGetPrimeMeridian(int srid)
/
/ returns the name of the Prime Meridian on success
/ NULL on failure or on invalid argument
*/
    int srid;
    char *prime_meridian = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    prime_meridian = srid_get_prime_meridian (sqlite, srid);
    if (prime_meridian == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, prime_meridian, strlen (prime_meridian),
			     free);
}

static void
fnct_SridGetProjection (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ SridGetProjection(int srid)
/
/ returns the name of the Projection on success
/ NULL on failure or on invalid argument
*/
    int srid;
    char *projection = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    projection = srid_get_projection (sqlite, srid);
    if (projection == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, projection, strlen (projection), free);
}

static void
fnct_SridGetDatum (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SridGetDatum(int srid)
/
/ returns the name of the Datum on success
/ NULL on failure or on invalid argument
*/
    int srid;
    char *datum = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    datum = srid_get_datum (sqlite, srid);
    if (datum == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, datum, strlen (datum), free);
}

static void
fnct_SridGetUnit (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SridGetUnit(int srid)
/
/ returns the name of the Spheroid on success
/ NULL on failure or on invalid argument
*/
    int srid;
    char *unit = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    unit = srid_get_unit (sqlite, srid);
    if (unit == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, unit, strlen (unit), free);
}

static void
common_srid_axis (sqlite3_context * context, int argc,
		  sqlite3_value ** argv, char axis, char mode)
{
/* commonn implentation - SRID Get Axis */
    int srid;
    char *result = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    result = srid_get_axis (sqlite, srid, axis, mode);
    if (result == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, result, strlen (result), free);
}

static void
fnct_SridGetAxis1Name (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ SridGetAxis_1_Name(int srid)
/
/ returns the name of the first Axis on success
/ NULL on failure or on invalid argument
*/
    common_srid_axis (context, argc, argv, SPLITE_AXIS_1, SPLITE_AXIS_NAME);
}

static void
fnct_SridGetAxis1Orientation (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ SridGetAxis_1_Orientation(int srid)
/
/ returns the orientation of the first Axis on success
/ NULL on failure or on invalid argument
*/
    common_srid_axis (context, argc, argv, SPLITE_AXIS_1,
		      SPLITE_AXIS_ORIENTATION);
}

static void
fnct_SridGetAxis2Name (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ SridGetAxis_2_Name(int srid)
/
/ returns the name of the second Axis on success
/ NULL on failure or on invalid argument
*/
    common_srid_axis (context, argc, argv, SPLITE_AXIS_2, SPLITE_AXIS_NAME);
}

static void
fnct_SridGetAxis2Orientation (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ SridGetAxis_2_Orientation(int srid)
/
/ returns the orientation of the second Axis on success
/ NULL on failure or on invalid argument
*/
    common_srid_axis (context, argc, argv, SPLITE_AXIS_2,
		      SPLITE_AXIS_ORIENTATION);
}

static int
recoverGeomColumn (sqlite3 * sqlite, const char *table,
		   const char *column, int xtype, int dims, int srid)
{
/* checks if TABLE.COLUMN exists and has the required features */
    int ok = 1;
    int type;
    sqlite3_stmt *stmt;
    gaiaGeomCollPtr geom;
    const void *blob_value;
    int len;
    int ret;
    int i_col;
    int is_nullable = 1;
    char *p_table;
    char *p_column;
    char *sql_statement;

/* testing if NOT NULL */
    p_table = gaiaDoubleQuotedSql (table);
    sql_statement = sqlite3_mprintf ("PRAGMA table_info(\"%s\")", p_table);
    free (p_table);
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("recoverGeomColumn: \"%s\"\n", sqlite3_errmsg (sqlite));
	  return 0;
      }
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	    {
		if (strcasecmp
		    (column, (const char *) sqlite3_column_text (stmt, 1)) == 0)
		  {
		      if (sqlite3_column_int (stmt, 2) != 0)
			  is_nullable = 0;
		  }
	    }
      }
    sqlite3_finalize (stmt);

    p_table = gaiaDoubleQuotedSql (table);
    p_column = gaiaDoubleQuotedSql (column);
    sql_statement =
	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\"", p_column, p_table);
    free (p_table);
    free (p_column);
/* compiling SQL prepared statement */
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("recoverGeomColumn: error %d \"%s\"\n",
			sqlite3_errcode (sqlite), sqlite3_errmsg (sqlite));
	  return 0;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
    sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	    {
		/* checking Geometry features */
		geom = NULL;
		for (i_col = 0; i_col < sqlite3_column_count (stmt); i_col++)
		  {
		      if (sqlite3_column_type (stmt, i_col) == SQLITE_NULL)
			{
			    /* found a NULL geometry */
			    if (!is_nullable)
				ok = 0;
			}
		      else if (sqlite3_column_type (stmt, i_col) != SQLITE_BLOB)
			  ok = 0;
		      else
			{
			    blob_value = sqlite3_column_blob (stmt, i_col);
			    len = sqlite3_column_bytes (stmt, i_col);
			    geom = gaiaFromSpatiaLiteBlobWkb (blob_value, len);
			    if (!geom)
				ok = 0;
			    else
			      {
				  if (geom->DimensionModel != dims)
				      ok = 0;
				  if (geom->Srid != srid)
				      ok = 0;
				  type = gaiaGeometryType (geom);
				  if (xtype == -1)
				      ;	/* GEOMETRY */
				  else
				    {
					if (xtype == type)
					    ;
					else
					    ok = 0;
				    }
				  gaiaFreeGeomColl (geom);
			      }
			}
		  }
	    }
	  if (!ok)
	      break;
      }
    ret = sqlite3_finalize (stmt);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("recoverGeomColumn: error %d \"%s\"\n",
			sqlite3_errcode (sqlite), sqlite3_errmsg (sqlite));
	  return 0;
      }
    return ok;
}

static void
fnct_AddGeometryColumn (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ AddGeometryColumn(table, column, srid, type [ , dimension  [  , not-null ] ] )
/
/ creates a new COLUMN of given TYPE into TABLE
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    const unsigned char *type;
    const unsigned char *txt_dims;
    int xtype;
    int srid = -1;
    int dimension = 2;
    int dims = -1;
    int auto_dims = -1;
    char sql[1024];
    int ret;
    int notNull = 0;
    int metadata_version;
    sqlite3_stmt *stmt;
    char *p_table = NULL;
    char *quoted_table;
    char *quoted_column;
    const char *p_type = NULL;
    const char *p_dims = NULL;
    int n_type = 0;
    int n_dims = 0;
    char *sql_statement;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 3 [SRID] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    srid = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 4 [geometry_type] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    type = sqlite3_value_text (argv[3]);
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		dimension = sqlite3_value_int (argv[4]);
		if (dimension == 2)
		    dims = GAIA_XY;
		if (dimension == 3)
		    dims = GAIA_XY_Z;
		if (dimension == 4)
		    dims = GAIA_XY_Z_M;
	    }
	  else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	    {
		txt_dims = sqlite3_value_text (argv[4]);
		if (strcasecmp ((char *) txt_dims, "XY") == 0)
		    dims = GAIA_XY;
		if (strcasecmp ((char *) txt_dims, "XYZ") == 0)
		    dims = GAIA_XY_Z;
		if (strcasecmp ((char *) txt_dims, "XYM") == 0)
		    dims = GAIA_XY_M;
		if (strcasecmp ((char *) txt_dims, "XYZM") == 0)
		    dims = GAIA_XY_Z_M;
	    }
	  else
	    {
		spatialite_e
		    ("AddGeometryColumn() error: argument 5 [dimension] is not of the Integer or Text type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
      }
    if (argc == 6)
      {
	  /* optional NOT NULL arg */
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		spatialite_e
		    ("AddGeometryColumn() error: argument 6 [not null] is not of the Integer type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  notNull = sqlite3_value_int (argv[5]);
      }
    xtype = GAIA_UNKNOWN;
    if (strcasecmp ((char *) type, "POINT") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRING") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGON") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINT") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRING") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGON") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTION") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRY") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = -1;
      }
    if (xtype == GAIA_UNKNOWN)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 4 [geometry_type] has an illegal value\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (dims < 0)
	dims = auto_dims;
    if (dims == GAIA_XY || dims == GAIA_XY_Z || dims == GAIA_XY_M
	|| dims == GAIA_XY_Z_M)
	;
    else
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 5 [dimension] ILLEGAL VALUE\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (auto_dims != GAIA_XY && dims != auto_dims)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: argument 5 [dimension] ILLEGAL VALUE\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* checking if the table exists */
    strcpy (sql,
	    "SELECT name FROM sqlite_master WHERE type = 'table' AND Lower(name) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("AddGeometryColumn: \"%s\"\n", sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	    {
		if (p_table != NULL)
		    sqlite3_free (p_table);
		p_table =
		    sqlite3_mprintf ("%s",
				     (const char *) sqlite3_column_text (stmt,
									 0));
	    }
      }
    sqlite3_finalize (stmt);
    if (!p_table)
      {
	  spatialite_e
	      ("AddGeometryColumn() error: table '%s' does not exist\n", table);
	  sqlite3_result_int (context, 0);
	  return;
      }
/* checking for WITHOUT ROWID */
    if (is_without_rowid_table (sqlite, table))
      {
	  spatialite_e
	      ("AddGeometryColumn() error: table '%s' is WITHOUT ROWID\n",
	       table);
	  sqlite3_result_int (context, 0);
	  return;
      }
    metadata_version = checkSpatialMetaData (sqlite);
    if (metadata_version == 1 || metadata_version == 3)
	;
    else
      {
	  spatialite_e
	      ("AddGeometryColumn() error: unexpected metadata layout\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* trying to add the column */
    switch (xtype)
      {
      case GAIA_POINT:
	  p_type = "POINT";
	  break;
      case GAIA_LINESTRING:
	  p_type = "LINESTRING";
	  break;
      case GAIA_POLYGON:
	  p_type = "POLYGON";
	  break;
      case GAIA_MULTIPOINT:
	  p_type = "MULTIPOINT";
	  break;
      case GAIA_MULTILINESTRING:
	  p_type = "MULTILINESTRING";
	  break;
      case GAIA_MULTIPOLYGON:
	  p_type = "MULTIPOLYGON";
	  break;
      case GAIA_GEOMETRYCOLLECTION:
	  p_type = "GEOMETRYCOLLECTION";
	  break;
      case -1:
	  p_type = "GEOMETRY";
	  break;
      };
    quoted_table = gaiaDoubleQuotedSql (p_table);
    quoted_column = gaiaDoubleQuotedSql (column);
    if (notNull)
      {
	  /* adding a NOT NULL clause */
	  sql_statement =
	      sqlite3_mprintf ("ALTER TABLE \"%s\" ADD COLUMN \"%s\" "
			       "%s NOT NULL DEFAULT ''", quoted_table,
			       quoted_column, p_type);
      }
    else
	sql_statement =
	    sqlite3_mprintf ("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s ",
			     quoted_table, quoted_column, p_type);
    free (quoted_table);
    free (quoted_column);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("AddGeometryColumn: \"%s\"\n", sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  sqlite3_free (p_table);
	  return;
      }
/* ok, inserting into geometry_columns [Spatial Metadata] */
    if (metadata_version == 1)
      {
	  /* legacy metadata style <= v.3.1.0 */
	  switch (xtype)
	    {
	    case GAIA_POINT:
		p_type = "POINT";
		break;
	    case GAIA_LINESTRING:
		p_type = "LINESTRING";
		break;
	    case GAIA_POLYGON:
		p_type = "POLYGON";
		break;
	    case GAIA_MULTIPOINT:
		p_type = "MULTIPOINT";
		break;
	    case GAIA_MULTILINESTRING:
		p_type = "MULTILINESTRING";
		break;
	    case GAIA_MULTIPOLYGON:
		p_type = "MULTIPOLYGON";
		break;
	    case GAIA_GEOMETRYCOLLECTION:
		p_type = "GEOMETRYCOLLECTION";
		break;
	    case -1:
		p_type = "GEOMETRY";
		break;
	    };
	  switch (dims)
	    {
	    case GAIA_XY:
		p_dims = "XY";
		break;
	    case GAIA_XY_Z:
		p_dims = "XYZ";
		break;
	    case GAIA_XY_M:
		p_dims = "XYM";
		break;
	    case GAIA_XY_Z_M:
		p_dims = "XYZM";
		break;
	    };
	  sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
					   "(f_table_name, f_geometry_column, type, coord_dimension, srid, "
					   "spatial_index_enabled) VALUES (?, ?, %Q, %Q, ?, 0)",
					   p_type, p_dims);
      }
    else
      {
	  /* current metadata style >= v.4.0.0 */
	  switch (xtype)
	    {
	    case GAIA_POINT:
		if (dims == GAIA_XY_Z)
		    n_type = 1001;
		else if (dims == GAIA_XY_M)
		    n_type = 2001;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3001;
		else
		    n_type = 1;
		break;
	    case GAIA_LINESTRING:
		if (dims == GAIA_XY_Z)
		    n_type = 1002;
		else if (dims == GAIA_XY_M)
		    n_type = 2002;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3002;
		else
		    n_type = 2;
		break;
	    case GAIA_POLYGON:
		if (dims == GAIA_XY_Z)
		    n_type = 1003;
		else if (dims == GAIA_XY_M)
		    n_type = 2003;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3003;
		else
		    n_type = 3;
		break;
	    case GAIA_MULTIPOINT:
		if (dims == GAIA_XY_Z)
		    n_type = 1004;
		else if (dims == GAIA_XY_M)
		    n_type = 2004;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3004;
		else
		    n_type = 4;
		break;
	    case GAIA_MULTILINESTRING:
		if (dims == GAIA_XY_Z)
		    n_type = 1005;
		else if (dims == GAIA_XY_M)
		    n_type = 2005;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3005;
		else
		    n_type = 5;
		break;
	    case GAIA_MULTIPOLYGON:
		if (dims == GAIA_XY_Z)
		    n_type = 1006;
		else if (dims == GAIA_XY_M)
		    n_type = 2006;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3006;
		else
		    n_type = 6;
		break;
	    case GAIA_GEOMETRYCOLLECTION:
		if (dims == GAIA_XY_Z)
		    n_type = 1007;
		else if (dims == GAIA_XY_M)
		    n_type = 2007;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3007;
		else
		    n_type = 7;
		break;
	    case -1:
		if (dims == GAIA_XY_Z)
		    n_type = 1000;
		else if (dims == GAIA_XY_M)
		    n_type = 2000;
		else if (dims == GAIA_XY_Z_M)
		    n_type = 3000;
		else
		    n_type = 0;
		break;
	    };
	  switch (dims)
	    {
	    case GAIA_XY:
		n_dims = 2;
		break;
	    case GAIA_XY_Z:
	    case GAIA_XY_M:
		n_dims = 3;
		break;
	    case GAIA_XY_Z_M:
		n_dims = 4;
		break;
	    };
	  sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
					   "(f_table_name, f_geometry_column, geometry_type, coord_dimension, "
					   "srid, spatial_index_enabled) VALUES (Lower(?), Lower(?), %d, %d, ?, 0)",
					   n_type, n_dims);
      }
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("AddGeometryColumn: \"%s\"\n", sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  sqlite3_free (p_table);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, p_table, strlen (p_table), SQLITE_STATIC);
    sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
    if (srid < 0)
	sqlite3_bind_int (stmt, 3, -1);
    else
	sqlite3_bind_int (stmt, 3, srid);
    ret = sqlite3_step (stmt);
    if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	;
    else
      {
	  spatialite_e ("AddGeometryColumn() error: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_finalize (stmt);
	  goto error;
      }
    sqlite3_finalize (stmt);
    if (metadata_version == 3)
      {
	  /* current metadata style >= v.4.0.0 */

	  /* inserting a row into GEOMETRY_COLUMNS_AUTH */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_auth (f_table_name, f_geometry_column, ");
	  strcat (sql, "read_only, hidden) VALUES (Lower(?), Lower(?), 0, 0)");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("AddGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		sqlite3_free (p_table);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("AddGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
	  /* inserting a row into GEOMETRY_COLUMNS_STATISTICS */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_statistics (f_table_name, f_geometry_column) ");
	  strcat (sql, "VALUES (Lower(?), Lower(?))");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("AddGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		sqlite3_free (p_table);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("AddGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
	  /* inserting a row into GEOMETRY_COLUMNS_TIME */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_time (f_table_name, f_geometry_column) ");
	  strcat (sql, "VALUES (Lower(?), Lower(?))");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("AddGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		sqlite3_free (p_table);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("AddGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    switch (xtype)
      {
      case GAIA_POINT:
	  p_type = "POINT";
	  break;
      case GAIA_LINESTRING:
	  p_type = "LINESTRING";
	  break;
      case GAIA_POLYGON:
	  p_type = "POLYGON";
	  break;
      case GAIA_MULTIPOINT:
	  p_type = "MULTIPOINT";
	  break;
      case GAIA_MULTILINESTRING:
	  p_type = "MULTILINESTRING";
	  break;
      case GAIA_MULTIPOLYGON:
	  p_type = "MULTIPOLYGON";
	  break;
      case GAIA_GEOMETRYCOLLECTION:
	  p_type = "GEOMETRYCOLLECTION";
	  break;
      case -1:
	  p_type = "GEOMETRY";
	  break;
      };
    switch (dims)
      {
      case GAIA_XY:
	  p_dims = "XY";
	  break;
      case GAIA_XY_Z:
	  p_dims = "XYZ";
	  break;
      case GAIA_XY_M:
	  p_dims = "XYM";
	  break;
      case GAIA_XY_Z_M:
	  p_dims = "XYZM";
	  break;
      };
    sql_statement =
	sqlite3_mprintf ("Geometry [%s,%s,SRID=%d] successfully created",
			 p_type, p_dims, (srid <= 0) ? -1 : srid);
    updateSpatiaLiteHistory (sqlite, table, column, sql_statement);
    sqlite3_free (sql_statement);
    sqlite3_free (p_table);
    return;
  error:
    sqlite3_result_int (context, 0);
    sqlite3_free (p_table);
    return;
}

static void
fnct_RecoverGeometryColumn (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ RecoverGeometryColumn(table, column, srid, type , dimension )
/
/ checks if an existing TABLE.COLUMN satisfies the required geometric features
/ if yes adds it to SpatialMetaData and enabling triggers
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    const unsigned char *type;
    int xtype;
    int xxtype;
    int srid = -1;
    const unsigned char *txt_dims;
    int dimension = 2;
    int dims = -1;
    int auto_dims = -1;
    char sql[1024];
    int ret;
    int metadata_version;
    sqlite3_stmt *stmt;
    int exists = 0;
    const char *p_type = NULL;
    const char *p_dims = NULL;
    int n_type = GAIA_UNKNOWN;
    int n_dims = -1;
    char *sql_statement;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 3 [SRID] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    srid = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 4 [geometry_type] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    type = sqlite3_value_text (argv[3]);
    if (argc == 5)
      {
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		dimension = sqlite3_value_int (argv[4]);
		if (dimension == 2)
		    dims = GAIA_XY;
		if (dimension == 3)
		    dims = GAIA_XY_Z;
		if (dimension == 4)
		    dims = GAIA_XY_Z_M;
	    }
	  else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	    {
		txt_dims = sqlite3_value_text (argv[4]);
		if (strcasecmp ((char *) txt_dims, "XY") == 0)
		    dims = GAIA_XY;
		if (strcasecmp ((char *) txt_dims, "XYZ") == 0)
		    dims = GAIA_XY_Z;
		if (strcasecmp ((char *) txt_dims, "XYM") == 0)
		    dims = GAIA_XY_M;
		if (strcasecmp ((char *) txt_dims, "XYZM") == 0)
		    dims = GAIA_XY_Z_M;
	    }
	  else
	    {
		spatialite_e
		    ("RecoverGeometryColumn() error: argument 5 [dimension] is not of the Integer or Text type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
      }
    xtype = GAIA_UNKNOWN;
    if (strcasecmp ((char *) type, "POINT") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRING") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGON") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINT") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRING") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGON") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTION") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRY") == 0)
      {
	  auto_dims = GAIA_XY;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYZ") == 0)
      {
	  auto_dims = GAIA_XY_Z;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYM") == 0)
      {
	  auto_dims = GAIA_XY_M;
	  xtype = -1;
      }
    if (strcasecmp ((char *) type, "POINTZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_POINT;
      }
    if (strcasecmp ((char *) type, "LINESTRINGZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_LINESTRING;
      }
    if (strcasecmp ((char *) type, "POLYGONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_POLYGON;
      }
    if (strcasecmp ((char *) type, "MULTIPOINTZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTIPOINT;
      }
    if (strcasecmp ((char *) type, "MULTILINESTRINGZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTILINESTRING;
      }
    if (strcasecmp ((char *) type, "MULTIPOLYGONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_MULTIPOLYGON;
      }
    if (strcasecmp ((char *) type, "GEOMETRYCOLLECTIONZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = GAIA_GEOMETRYCOLLECTION;
      }
    if (strcasecmp ((char *) type, "GEOMETRYZM") == 0)
      {
	  auto_dims = GAIA_XY_Z_M;
	  xtype = -1;
      }
    if (dims < 0)
	dims = auto_dims;
    if (xtype == GAIA_UNKNOWN)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 4 [geometry_type] has an illegal value\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (dims == GAIA_XY || dims == GAIA_XY_Z || dims == GAIA_XY_M
	|| dims == GAIA_XY_Z_M)
	;
    else
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 5 [dimension] ILLEGAL VALUE\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (auto_dims != GAIA_XY && dims != auto_dims)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: argument 5 [dimension] ILLEGAL VALUE\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    metadata_version = checkSpatialMetaData (sqlite);
    if (metadata_version == 1 || metadata_version == 3)
	;
    else
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: unexpected metadata layout\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* checking if the table exists */
    strcpy (sql,
	    "SELECT name FROM sqlite_master WHERE type = 'table' AND Lower(name) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	    {
		if (sqlite3_column_type (stmt, 0) == SQLITE_TEXT)
		    exists = 1;
	    }
      }
    sqlite3_finalize (stmt);
    if (!exists)
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: table '%s' does not exist\n",
	       table);
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (is_without_rowid_table (sqlite, table))
      {
	  spatialite_e
	      ("RecoverGeometryColumn() error: table '%s' is WITHOUT ROWID\n",
	       table);
	  sqlite3_result_int (context, 0);
	  return;
      }
/* adjusting the actual GeometryType */
    xxtype = xtype;
    xtype = GAIA_UNKNOWN;
    if (xxtype == GAIA_POINT)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_POINTZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_POINTM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_POINTZM;
		break;
	    default:
		xtype = GAIA_POINT;
		break;
	    };
      }
    if (xxtype == GAIA_LINESTRING)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_LINESTRINGZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_LINESTRINGM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_LINESTRINGZM;
		break;
	    default:
		xtype = GAIA_LINESTRING;
		break;
	    };
      }
    if (xxtype == GAIA_POLYGON)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_POLYGONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_POLYGONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_POLYGONZM;
		break;
	    default:
		xtype = GAIA_POLYGON;
		break;
	    };
      }
    if (xxtype == GAIA_MULTIPOINT)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTIPOINTZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTIPOINTM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTIPOINTZM;
		break;
	    default:
		xtype = GAIA_MULTIPOINT;
		break;
	    };
      }
    if (xxtype == GAIA_MULTILINESTRING)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTILINESTRINGZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTILINESTRINGM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTILINESTRINGZM;
		break;
	    default:
		xtype = GAIA_MULTILINESTRING;
		break;
	    };
      }
    if (xxtype == GAIA_MULTIPOLYGON)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_MULTIPOLYGONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_MULTIPOLYGONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_MULTIPOLYGONZM;
		break;
	    default:
		xtype = GAIA_MULTIPOLYGON;
		break;
	    };
      }
    if (xxtype == GAIA_GEOMETRYCOLLECTION)
      {
	  switch (dims)
	    {
	    case GAIA_XY_Z:
		xtype = GAIA_GEOMETRYCOLLECTIONZ;
		break;
	    case GAIA_XY_M:
		xtype = GAIA_GEOMETRYCOLLECTIONM;
		break;
	    case GAIA_XY_Z_M:
		xtype = GAIA_GEOMETRYCOLLECTIONZM;
		break;
	    default:
		xtype = GAIA_GEOMETRYCOLLECTION;
		break;
	    };
      }
    if (xxtype == -1)
	xtype = -1;		/* GEOMETRY */
    if (!recoverGeomColumn (sqlite, table, column, xtype, dims, srid))
      {
	  spatialite_e ("RecoverGeometryColumn(): validation failed\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* deleting anyway any previous definition */
    sql_statement = sqlite3_mprintf ("DELETE FROM geometry_columns "
				     "WHERE Lower(f_table_name) = Lower(?) AND "
				     "Lower(f_geometry_column) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
    sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
    ret = sqlite3_step (stmt);
    if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	;
    else
      {
	  spatialite_e ("RecoverGeometryColumn() error: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_finalize (stmt);
	  goto error;
      }
    sqlite3_finalize (stmt);

    if (metadata_version == 1)
      {
	  /* legacy metadata style <= v.3.1.0 */
	  switch (xtype)
	    {
	    case GAIA_POINT:
	    case GAIA_POINTZ:
	    case GAIA_POINTM:
	    case GAIA_POINTZM:
		p_type = "POINT";
		break;
	    case GAIA_LINESTRING:
	    case GAIA_LINESTRINGZ:
	    case GAIA_LINESTRINGM:
	    case GAIA_LINESTRINGZM:
		p_type = "LINESTRING";
		break;
	    case GAIA_POLYGON:
	    case GAIA_POLYGONZ:
	    case GAIA_POLYGONM:
	    case GAIA_POLYGONZM:
		p_type = "POLYGON";
		break;
	    case GAIA_MULTIPOINT:
	    case GAIA_MULTIPOINTZ:
	    case GAIA_MULTIPOINTM:
	    case GAIA_MULTIPOINTZM:
		p_type = "MULTIPOINT";
		break;
	    case GAIA_MULTILINESTRING:
	    case GAIA_MULTILINESTRINGZ:
	    case GAIA_MULTILINESTRINGM:
	    case GAIA_MULTILINESTRINGZM:
		p_type = "MULTILINESTRING";
		break;
	    case GAIA_MULTIPOLYGON:
	    case GAIA_MULTIPOLYGONZ:
	    case GAIA_MULTIPOLYGONM:
	    case GAIA_MULTIPOLYGONZM:
		p_type = "MULTIPOLYGON";
		break;
	    case GAIA_GEOMETRYCOLLECTION:
	    case GAIA_GEOMETRYCOLLECTIONZ:
	    case GAIA_GEOMETRYCOLLECTIONM:
	    case GAIA_GEOMETRYCOLLECTIONZM:
		p_type = "GEOMETRYCOLLECTION";
		break;
	    case -1:
		p_type = "GEOMETRY";
		break;
	    };
	  strcat (sql, "', '");
	  switch (dims)
	    {
	    case GAIA_XY:
		p_dims = "XY";
		break;
	    case GAIA_XY_Z:
		p_dims = "XYZ";
		break;
	    case GAIA_XY_M:
		p_dims = "XYM";
		break;
	    case GAIA_XY_Z_M:
		p_dims = "XYZM";
		break;
	    };
/* Sandro 2013-01-07
/ fixing an issue reported by Peter Aronson [ESRI] <paronson@esri.com>
	  sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
					   "(f_table_name, f_geometry_column, type, coord_dimension, srid, "
					   "spatial_index_enabled) VALUES (Lower(?), Lower(?), %Q, %Q, ?, 0)",
					   p_type, p_dims);
*/
	  sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
					   "(f_table_name, f_geometry_column, type, coord_dimension, srid, "
					   "spatial_index_enabled) VALUES (?, ?, %Q, %Q, ?, 0)",
					   p_type, p_dims);
      }
    else
      {
	  /* current metadata style >= v.4.0.0 */
	  switch (xtype)
	    {
	    case GAIA_POINT:
		n_type = 1;
		n_dims = 2;
		break;
	    case GAIA_POINTZ:
		n_type = 1001;
		n_dims = 3;
		break;
	    case GAIA_POINTM:
		n_type = 2001;
		n_dims = 3;
		break;
	    case GAIA_POINTZM:
		n_type = 3001;
		n_dims = 4;
		break;
	    case GAIA_LINESTRING:
		n_type = 2;
		n_dims = 2;
		break;
	    case GAIA_LINESTRINGZ:
		n_type = 1002;
		n_dims = 3;
		break;
	    case GAIA_LINESTRINGM:
		n_type = 2002;
		n_dims = 3;
		break;
	    case GAIA_LINESTRINGZM:
		n_type = 3002;
		n_dims = 4;
		break;
	    case GAIA_POLYGON:
		n_type = 3;
		n_dims = 2;
		break;
	    case GAIA_POLYGONZ:
		n_type = 1003;
		n_dims = 3;
		break;
	    case GAIA_POLYGONM:
		n_type = 2003;
		n_dims = 3;
		break;
	    case GAIA_POLYGONZM:
		n_type = 3003;
		n_dims = 4;
		break;
	    case GAIA_MULTIPOINT:
		n_type = 4;
		n_dims = 2;
		break;
	    case GAIA_MULTIPOINTZ:
		n_type = 1004;
		n_dims = 3;
		break;
	    case GAIA_MULTIPOINTM:
		n_type = 2004;
		n_dims = 3;
		break;
	    case GAIA_MULTIPOINTZM:
		n_type = 3004;
		n_dims = 4;
		break;
	    case GAIA_MULTILINESTRING:
		n_type = 5;
		n_dims = 2;
		break;
	    case GAIA_MULTILINESTRINGZ:
		n_type = 1005;
		n_dims = 3;
		break;
	    case GAIA_MULTILINESTRINGM:
		n_type = 2005;
		n_dims = 3;
		break;
	    case GAIA_MULTILINESTRINGZM:
		n_type = 3005;
		n_dims = 4;
		break;
	    case GAIA_MULTIPOLYGON:
		n_type = 6;
		n_dims = 2;
		break;
	    case GAIA_MULTIPOLYGONZ:
		n_type = 1006;
		n_dims = 3;
		break;
	    case GAIA_MULTIPOLYGONM:
		n_type = 2006;
		n_dims = 3;
		break;
	    case GAIA_MULTIPOLYGONZM:
		n_type = 3006;
		n_dims = 4;
		break;
	    case GAIA_GEOMETRYCOLLECTION:
		n_type = 7;
		n_dims = 2;
		break;
	    case GAIA_GEOMETRYCOLLECTIONZ:
		n_type = 1007;
		n_dims = 3;
		break;
	    case GAIA_GEOMETRYCOLLECTIONM:
		n_type = 2007;
		n_dims = 3;
		break;
	    case GAIA_GEOMETRYCOLLECTIONZM:
		n_type = 3007;
		n_dims = 4;
		break;
	    case -1:
		switch (dims)
		  {
		  case GAIA_XY:
		      n_type = 0;
		      n_dims = 2;
		      break;
		  case GAIA_XY_Z:
		      n_type = 1000;
		      n_dims = 3;
		      break;
		  case GAIA_XY_M:
		      n_type = 2000;
		      n_dims = 3;
		      break;
		  case GAIA_XY_Z_M:
		      n_type = 3000;
		      n_dims = 4;
		      break;
		  };
		break;
	    };
	  sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
					   "(f_table_name, f_geometry_column, geometry_type, coord_dimension, "
					   "srid, spatial_index_enabled) VALUES (Lower(?), Lower(?), %d, %d, ?, 0)",
					   n_type, n_dims);
      }
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
    sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
    if (srid < 0)
	sqlite3_bind_int (stmt, 3, -1);
    else
	sqlite3_bind_int (stmt, 3, srid);
    ret = sqlite3_step (stmt);
    if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	;
    else
      {
	  spatialite_e ("RecoverGeometryColumn() error: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_finalize (stmt);
	  goto error;
      }
    sqlite3_finalize (stmt);
    if (metadata_version == 3)
      {
	  /* current metadata style >= v.4.0.0 */

	  /* inserting a row into GEOMETRY_COLUMNS_AUTH */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_auth (f_table_name, f_geometry_column, ");
	  strcat (sql, "read_only, hidden) VALUES (Lower(?), Lower(?), 0, 0)");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("RecoverGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
	  /* inserting a row into GEOMETRY_COLUMNS_STATISTICS */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_statistics (f_table_name, f_geometry_column) ");
	  strcat (sql, "VALUES (Lower(?), Lower(?))");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("RecoverGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
	  /* inserting a row into GEOMETRY_COLUMNS_TIME */
	  strcpy (sql,
		  "INSERT OR REPLACE INTO geometry_columns_time (f_table_name, f_geometry_column) ");
	  strcat (sql, "VALUES (Lower(?), Lower(?))");
	  ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("RecoverGeometryColumn: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_result_int (context, 0);
		return;
	    }
	  sqlite3_reset (stmt);
	  sqlite3_clear_bindings (stmt);
	  sqlite3_bind_text (stmt, 1, table, strlen (table), SQLITE_STATIC);
	  sqlite3_bind_text (stmt, 2, column, strlen (column), SQLITE_STATIC);
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	      ;
	  else
	    {
		spatialite_e ("RecoverGeometryColumn() error: \"%s\"\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto error;
	    }
	  sqlite3_finalize (stmt);
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    switch (xtype)
      {
      case GAIA_POINT:
      case GAIA_POINTZ:
      case GAIA_POINTM:
      case GAIA_POINTZM:
	  p_type = "POINT";
	  break;
      case GAIA_LINESTRING:
      case GAIA_LINESTRINGZ:
      case GAIA_LINESTRINGM:
      case GAIA_LINESTRINGZM:
	  p_type = "LINESTRING";
	  break;
      case GAIA_POLYGON:
      case GAIA_POLYGONZ:
      case GAIA_POLYGONM:
      case GAIA_POLYGONZM:
	  p_type = "POLYGON";
	  break;
      case GAIA_MULTIPOINT:
      case GAIA_MULTIPOINTZ:
      case GAIA_MULTIPOINTM:
      case GAIA_MULTIPOINTZM:
	  p_type = "MULTIPOINT";
	  break;
      case GAIA_MULTILINESTRING:
      case GAIA_MULTILINESTRINGZ:
      case GAIA_MULTILINESTRINGM:
      case GAIA_MULTILINESTRINGZM:
	  p_type = "MULTILINESTRING";
	  break;
      case GAIA_MULTIPOLYGON:
      case GAIA_MULTIPOLYGONZ:
      case GAIA_MULTIPOLYGONM:
      case GAIA_MULTIPOLYGONZM:
	  p_type = "MULTIPOLYGON";
	  break;
      case GAIA_GEOMETRYCOLLECTION:
      case GAIA_GEOMETRYCOLLECTIONZ:
      case GAIA_GEOMETRYCOLLECTIONM:
      case GAIA_GEOMETRYCOLLECTIONZM:
	  p_type = "GEOMETRYCOLLECTION";
	  break;
      case -1:
	  p_type = "GEOMETRY";
	  break;
      };
    switch (dims)
      {
      case GAIA_XY:
	  p_dims = "XY";
	  break;
      case GAIA_XY_Z:
	  p_dims = "XYZ";
	  break;
      case GAIA_XY_M:
	  p_dims = "XYM";
	  break;
      case GAIA_XY_Z_M:
	  p_dims = "XYZM";
	  break;
      };
    sql_statement =
	sqlite3_mprintf ("Geometry [%s,%s,SRID=%d] successfully recovered",
			 p_type, p_dims, (srid <= 0) ? -1 : srid);
    updateSpatiaLiteHistory (sqlite, table, column, sql_statement);
    sqlite3_free (sql_statement);
    return;
  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_DiscardGeometryColumn (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ DiscardGeometryColumn(table, column)
/
/ removes TABLE.COLUMN from the Spatial MetaData [thus disabling triggers too]
/ returns 1 on success
/ 0 on failure
*/
    const unsigned char *table;
    const unsigned char *column;
    char *p_table = NULL;
    char *p_column = NULL;
    sqlite3_stmt *stmt;
    char *sql_statement;
    char *raw;
    char *quoted;
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DiscardGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DiscardGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = sqlite3_value_text (argv[1]);

    sql_statement = sqlite3_mprintf ("DELETE FROM geometry_columns "
				     "WHERE Lower(f_table_name) = Lower(?) "
				     "AND Lower(f_geometry_column) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("DiscardGeometryColumn: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, (const char *) table,
		       strlen ((const char *) table), SQLITE_STATIC);
    sqlite3_bind_text (stmt, 2, (const char *) column,
		       strlen ((const char *) column), SQLITE_STATIC);
    ret = sqlite3_step (stmt);
    if (ret == SQLITE_DONE || ret == SQLITE_ROW)
	;
    else
      {
	  spatialite_e ("DiscardGeometryColumn() error: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_finalize (stmt);
	  goto error;
      }
    sqlite3_finalize (stmt);
/* removing triggers too */
    if (!getRealSQLnames
	(sqlite, (const char *) table, (const char *) column, &p_table,
	 &p_column))
      {
	  spatialite_e
	      ("DiscardGeometryColumn() error: not existing Table or Column\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    raw = sqlite3_mprintf ("ggi_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("ggu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gii_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("giu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gid_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gci_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gcu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gcd_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("tmi_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("tmu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("tmd_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;

    /* trying to delete old versions [v2.0, v2.2] triggers[if any] */
    raw = sqlite3_mprintf ("gti_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gtu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gsi_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    raw = sqlite3_mprintf ("gsu_%s_%s", p_table, p_column);
    quoted = gaiaDoubleQuotedSql (raw);
    sqlite3_free (raw);
    sql_statement =
	sqlite3_mprintf ("DROP TRIGGER IF EXISTS main.\"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    /* end deletion old versions [v2.0, v2.2] triggers[if any] */

    sqlite3_result_int (context, 1);
    updateSpatiaLiteHistory (sqlite, p_table,
			     p_column, "Geometry successfully discarded");
    free (p_table);
    free (p_column);
    return;
  error:
    if (p_table)
	free (p_table);
    if (p_column)
	free (p_column);
    spatialite_e ("DiscardGeometryColumn() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static int
registerVirtual (sqlite3 * sqlite, const char *table)
{
/* attempting to register a VirtualGeometry */
    char gtype[64];
    int xtype = -1;
    int srid = -1;
    char **results;
    int ret;
    int rows;
    int columns;
    int i;
    char *errMsg = NULL;
    int ok_virt_name = 0;
    int ok_virt_geometry = 0;
    int ok_srid = 0;
    int ok_geometry_type = 0;
    int ok_type = 0;
    int ok_coord_dimension = 0;
    int xdims;
    char *quoted;
    char *sql_statement;

/* testing the layout of virts_geometry_columns table */
    ret = sqlite3_get_table (sqlite,
			     "PRAGMA table_info(virts_geometry_columns)",
			     &results, &rows, &columns, &errMsg);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RegisterVirtualGeometry() error: \"%s\"\n", errMsg);
	  sqlite3_free (errMsg);
	  return 0;
      }
    for (i = 1; i <= rows; i++)
      {
	  if (strcasecmp ("virt_name", results[(i * columns) + 1]) == 0)
	      ok_virt_name = 1;
	  if (strcasecmp ("virt_geometry", results[(i * columns) + 1]) == 0)
	      ok_virt_geometry = 1;
	  if (strcasecmp ("srid", results[(i * columns) + 1]) == 0)
	      ok_srid = 1;
	  if (strcasecmp ("geometry_type", results[(i * columns) + 1]) == 0)
	      ok_geometry_type = 1;
	  if (strcasecmp ("type", results[(i * columns) + 1]) == 0)
	      ok_type = 1;
	  if (strcasecmp ("coord_dimension", results[(i * columns) + 1]) == 0)
	      ok_coord_dimension = 1;
      }
    sqlite3_free_table (results);

    if (ok_virt_name && ok_virt_geometry && ok_srid && ok_geometry_type
	&& ok_coord_dimension)
	;
    else if (ok_virt_name && ok_virt_geometry && ok_srid && ok_type)
	;
    else
	return 0;

/* determining Geometry Type and dims */
    quoted = gaiaDoubleQuotedSql (table);
    sql_statement =
	sqlite3_mprintf ("SELECT DISTINCT "
			 "ST_GeometryType(Geometry), ST_Srid(Geometry) FROM \"%s\"",
			 quoted);
    free (quoted);
    ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows, &columns,
			     &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RegisterVirtualGeometry() error: \"%s\"\n", errMsg);
	  sqlite3_free (errMsg);
	  return 0;
      }
    for (i = 1; i <= rows; i++)
      {
	  if (results[(i * columns)] == NULL)
	      *gtype = '\0';
	  else
	      strcpy (gtype, results[(i * columns)]);
	  if (results[(i * columns) + 1] == NULL)
	      srid = 0;
	  else
	      srid = atoi (results[(i * columns) + 1]);
      }
    sqlite3_free_table (results);

/* normalized Geometry type */
    if (strcmp (gtype, "POINT") == 0)
	xtype = 1;
    if (strcmp (gtype, "POINT Z") == 0)
	xtype = 1001;
    if (strcmp (gtype, "POINT M") == 0)
	xtype = 2001;
    if (strcmp (gtype, "POINT ZM") == 0)
	xtype = 3001;
    if (strcmp (gtype, "LINESTRING") == 0)
	xtype = 2;
    if (strcmp (gtype, "LINESTRING Z") == 0)
	xtype = 1002;
    if (strcmp (gtype, "LINESTRING M") == 0)
	xtype = 2002;
    if (strcmp (gtype, "LINESTRING ZM") == 0)
	xtype = 3002;
    if (strcmp (gtype, "POLYGON") == 0)
	xtype = 3;
    if (strcmp (gtype, "POLYGON Z") == 0)
	xtype = 1003;
    if (strcmp (gtype, "POLYGON M") == 0)
	xtype = 2003;
    if (strcmp (gtype, "POLYGON ZM") == 0)
	xtype = 3003;
    if (strcmp (gtype, "MULTIPOINT") == 0)
	xtype = 4;
    if (strcmp (gtype, "MULTIPOINT Z") == 0)
	xtype = 1004;
    if (strcmp (gtype, "MULTIPOINT M") == 0)
	xtype = 2004;
    if (strcmp (gtype, "MULTIPOINT ZM") == 0)
	xtype = 3004;
    if (strcmp (gtype, "MULTILINESTRING") == 0)
	xtype = 5;
    if (strcmp (gtype, "MULTILINESTRING Z") == 0)
	xtype = 1005;
    if (strcmp (gtype, "MULTILINESTRING M") == 0)
	xtype = 2005;
    if (strcmp (gtype, "MULTILINESTRING ZM") == 0)
	xtype = 3005;
    if (strcmp (gtype, "MULTIPOLYGON") == 0)
	xtype = 6;
    if (strcmp (gtype, "MULTIPOLYGON Z") == 0)
	xtype = 1006;
    if (strcmp (gtype, "MULTIPOLYGON M") == 0)
	xtype = 2006;
    if (strcmp (gtype, "MULTIPOLYGON ZM") == 0)
	xtype = 3006;

/* updating metadata tables */
    xdims = -1;
    switch (xtype)
      {
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
	  xdims = 2;
	  break;
      case 1001:
      case 1002:
      case 1003:
      case 1004:
      case 1005:
      case 1006:
      case 2001:
      case 2002:
      case 2003:
      case 2004:
      case 2005:
      case 2006:
	  xdims = 3;
	  break;
      case 3001:
      case 3002:
      case 3003:
      case 3004:
      case 3005:
      case 3006:
	  xdims = 4;
	  break;
      };
    if (ok_geometry_type)
      {
	  /* has the "geometry_type" column */
	  sql_statement =
	      sqlite3_mprintf
	      ("INSERT OR REPLACE INTO virts_geometry_columns "
	       "(virt_name, virt_geometry, geometry_type, coord_dimension, srid) "
	       "VALUES (Lower(%Q), 'geometry', %d, %d, %d)", table, xtype,
	       xdims, srid);
      }
    else
      {
	  /* has the "type" column */
	  const char *xgtype = "UNKNOWN";
	  switch (xtype)
	    {
	    case 1:
	    case 1001:
	    case 2001:
	    case 3001:
		xgtype = "POINT";
		break;
	    case 2:
	    case 1002:
	    case 2002:
	    case 3002:
		xgtype = "LINESTRING";
		break;
	    case 3:
	    case 1003:
	    case 2003:
	    case 3003:
		xgtype = "POLYGON";
		break;
	    case 4:
	    case 1004:
	    case 2004:
	    case 3004:
		xgtype = "MULTIPOINT";
		break;
	    case 5:
	    case 1005:
	    case 2005:
	    case 3005:
		xgtype = "MULTILINESTRING";
		break;
	    case 6:
	    case 1006:
	    case 2006:
	    case 3006:
		xgtype = "MULTIPOLYGON";
		break;
	    };
	  sql_statement =
	      sqlite3_mprintf
	      ("INSERT OR REPLACE INTO virts_geometry_columns "
	       "(virt_name, virt_geometry, type, srid) "
	       "VALUES (Lower(%Q), 'geometry', %Q, %d)", table, xgtype, srid);
      }
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RegisterVirtualGeometry() error: \"%s\"\n", errMsg);
	  sqlite3_free (errMsg);
	  return 0;
      }
    if (checkSpatialMetaData (sqlite) == 3)
      {
	  /* current metadata style >= v.4.0.0 */

	  /* inserting a row into VIRTS_GEOMETRY_COLUMNS_AUTH */
	  sql_statement = sqlite3_mprintf ("INSERT OR REPLACE INTO "
					   "virts_geometry_columns_auth (virt_name, virt_geometry, hidden) "
					   "VALUES (Lower(%Q), 'geometry', 0)",
					   table);
	  ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
	  sqlite3_free (sql_statement);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("RegisterVirtualGeometry() error: \"%s\"\n",
			      errMsg);
		sqlite3_free (errMsg);
		return 0;
	    }
	  /* inserting a row into GEOMETRY_COLUMNS_STATISTICS */
	  sql_statement = sqlite3_mprintf ("INSERT OR REPLACE INTO "
					   "virts_geometry_columns_statistics (virt_name, virt_geometry) "
					   "VALUES (Lower(%Q), 'geometry')",
					   table);
	  ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
	  sqlite3_free (sql_statement);
	  if (ret != SQLITE_OK)
	    {
		spatialite_e ("RegisterVirtualGeometry() error: \"%s\"\n",
			      errMsg);
		sqlite3_free (errMsg);
		return 0;
	    }
      }
    return 1;
}

static void
fnct_RegisterVirtualGeometry (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVirtualGeometry(table)
/
/ insert/updates TABLE.COLUMN into the Spatial MetaData [Virtual Table]
/ returns 1 on success
/ 0 on failure
*/
    const unsigned char *table;
    char sql[1024];
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RegisterVirtualGeometry() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    if (!registerVirtual (sqlite, (char *) table))
	goto error;
    sqlite3_result_int (context, 1);
    strcpy (sql, "Virtual Geometry successfully registered");
    updateSpatiaLiteHistory (sqlite, (const char *) table, "Geometry", sql);
    return;
  error:
    spatialite_e ("RegisterVirtualGeometry() error\n");
    sqlite3_result_int (context, 0);
    return;
}


static void
fnct_DropVirtualGeometry (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ DropVirtualGeometry(table)
/
/ removes TABLE.COLUMN from the Spatial MetaData and DROPs the Virtual Table
/ returns 1 on success
/ 0 on failure
*/
    const unsigned char *table;
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    char *quoted;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DropVirtualGeometry() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    sql_statement = sqlite3_mprintf ("DELETE FROM virts_geometry_columns "
				     "WHERE Lower(virt_name) = Lower(%Q)",
				     table);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    quoted = gaiaDoubleQuotedSql ((const char *) table);
    sql_statement = sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\"", quoted);
    free (quoted);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_result_int (context, 1);
    updateSpatiaLiteHistory (sqlite, (const char *) table, "Geometry",
			     "Virtual Geometry successfully dropped");
    return;
  error:
    spatialite_e ("DropVirtualGeometry() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_InitFDOSpatialMetaData (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ InitFDOSpatialMetaData(void)
/
/ creates the FDO-styled SPATIAL_REF_SYS and GEOMETRY_COLUMNS tables
/ returns 1 on success
/ 0 on failure
*/
    char sql[1024];
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* creating the SPATIAL_REF_SYS tables */
    strcpy (sql, "CREATE TABLE spatial_ref_sys (\n");
    strcat (sql, "srid INTEGER PRIMARY KEY,\n");
    strcat (sql, "auth_name TEXT,\n");
    strcat (sql, "auth_srid INTEGER,\n");
    strcat (sql, "srtext TEXT)");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
/* creating the GEOMETRY_COLUMN tables */
    strcpy (sql, "CREATE TABLE geometry_columns (\n");
    strcat (sql, "f_table_name TEXT,\n");
    strcat (sql, "f_geometry_column TEXT,\n");
    strcat (sql, "geometry_type INTEGER,\n");
    strcat (sql, "coord_dimension INTEGER,\n");
    strcat (sql, "srid INTEGER,\n");
    strcat (sql, "geometry_format TEXT)");
    ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("InitFDOSpatiaMetaData() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static int
recoverFDOGeomColumn (sqlite3 * sqlite, const unsigned char *table,
		      const unsigned char *column, int xtype, int srid)
{
/* checks if TABLE.COLUMN exists and has the required features */
    int ok = 1;
    char *sql_statement;
    int type;
    sqlite3_stmt *stmt;
    gaiaGeomCollPtr geom;
    const void *blob_value;
    int len;
    int ret;
    int i_col;
    char *xcolumn;
    char *xtable;
    xcolumn = gaiaDoubleQuotedSql ((char *) column);
    xtable = gaiaDoubleQuotedSql ((char *) table);
    sql_statement =
	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\"", xcolumn, xtable);
    free (xcolumn);
    free (xtable);
/* compiling SQL prepared statement */
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("recoverFDOGeomColumn: error %d \"%s\"\n",
			sqlite3_errcode (sqlite), sqlite3_errmsg (sqlite));
	  return 0;
      }
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	    {
		/* checking Geometry features */
		geom = NULL;
		for (i_col = 0; i_col < sqlite3_column_count (stmt); i_col++)
		  {
		      if (sqlite3_column_type (stmt, i_col) != SQLITE_BLOB)
			  ok = 0;
		      else
			{
			    blob_value = sqlite3_column_blob (stmt, i_col);
			    len = sqlite3_column_bytes (stmt, i_col);
			    geom = gaiaFromSpatiaLiteBlobWkb (blob_value, len);
			    if (!geom)
				ok = 0;
			    else
			      {
				  if (geom->Srid != srid)
				      ok = 0;
				  /* normalizing Geometry Type */
				  switch (gaiaGeometryType (geom))
				    {
				    case GAIA_POINT:
				    case GAIA_POINTZ:
				    case GAIA_POINTM:
				    case GAIA_POINTZM:
					type = GAIA_POINT;
					break;
				    case GAIA_LINESTRING:
				    case GAIA_LINESTRINGZ:
				    case GAIA_LINESTRINGM:
				    case GAIA_LINESTRINGZM:
					type = GAIA_LINESTRING;
					break;
				    case GAIA_POLYGON:
				    case GAIA_POLYGONZ:
				    case GAIA_POLYGONM:
				    case GAIA_POLYGONZM:
					type = GAIA_POLYGON;
					break;
				    case GAIA_MULTIPOINT:
				    case GAIA_MULTIPOINTZ:
				    case GAIA_MULTIPOINTM:
				    case GAIA_MULTIPOINTZM:
					type = GAIA_MULTIPOINT;
					break;
				    case GAIA_MULTILINESTRING:
				    case GAIA_MULTILINESTRINGZ:
				    case GAIA_MULTILINESTRINGM:
				    case GAIA_MULTILINESTRINGZM:
					type = GAIA_MULTILINESTRING;
					break;
				    case GAIA_MULTIPOLYGON:
				    case GAIA_MULTIPOLYGONZ:
				    case GAIA_MULTIPOLYGONM:
				    case GAIA_MULTIPOLYGONZM:
					type = GAIA_MULTIPOLYGON;
					break;
				    case GAIA_GEOMETRYCOLLECTION:
				    case GAIA_GEOMETRYCOLLECTIONZ:
				    case GAIA_GEOMETRYCOLLECTIONM:
				    case GAIA_GEOMETRYCOLLECTIONZM:
					type = GAIA_GEOMETRYCOLLECTION;
					break;
				    default:
					type = -1;
					break;
				    };
				  if (xtype == type)
				      ;
				  else
				      ok = 0;
				  gaiaFreeGeomColl (geom);
			      }
			}
		  }
	    }
	  if (!ok)
	      break;
      }
    ret = sqlite3_finalize (stmt);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("recoverFDOGeomColumn: error %d \"%s\"\n",
			sqlite3_errcode (sqlite), sqlite3_errmsg (sqlite));
	  return 0;
      }
    return ok;
}

static void
fnct_AddFDOGeometryColumn (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ AddFDOGeometryColumn(table, column, srid, geometry_type , dimension, geometry_format )
/
/ creates a new COLUMN of given TYPE into TABLE
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    const char *format;
    char xformat[64];
    int type;
    int srid = -1;
    int dimension = 2;
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    char **results;
    int rows;
    int columns;
    int i;
    int oktbl;
    char *xtable;
    char *xcolumn;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 3 [SRID] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    srid = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 4 [geometry_type] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    type = sqlite3_value_int (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 5 [dimension] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    dimension = sqlite3_value_int (argv[4]);
    if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 6 [geometry_format] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    format = (const char *) sqlite3_value_text (argv[5]);
    if (type ==
	GAIA_POINT
	|| type ==
	GAIA_LINESTRING
	|| type ==
	GAIA_POLYGON
	|| type ==
	GAIA_MULTIPOINT
	|| type ==
	GAIA_MULTILINESTRING
	|| type == GAIA_MULTIPOLYGON || type == GAIA_GEOMETRYCOLLECTION)
	;
    else
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 4 [geometry_type] has an illegal value\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (dimension < 2 || dimension > 4)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 5 [dimension] current version only accepts dimension=2,3,4\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (strcasecmp (format, "WKT") == 0)
	strcpy (xformat, "WKT");
    else if (strcasecmp (format, "WKB") == 0)
	strcpy (xformat, "WKB");
    else if (strcasecmp (format, "FGF") == 0)
	strcpy (xformat, "FGF");
    else if (strcasecmp (format, "SPATIALITE") == 0)
	strcpy (xformat, "SPATIALITE");
    else
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: argument 6 [geometry_format] has to be one of: WKT,WKB,FGF,SPATIALITE\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* checking if the table exists */
    xtable = gaiaDoubleQuotedSql (table);
    xcolumn = gaiaDoubleQuotedSql (column);
    sql_statement = sqlite3_mprintf ("SELECT name FROM sqlite_master "
				     "WHERE type = 'table' AND Upper(name) = Upper(%Q)",
				     table);
    free (xtable);
    free (xcolumn);
    ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows, &columns,
			     &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("AddFDOGeometryColumn: \"%s\"\n", errMsg);
	  sqlite3_free (errMsg);
	  return;
      }
    oktbl = 0;
    for (i = 1; i <= rows; i++)
	oktbl = 1;
    sqlite3_free_table (results);
    if (!oktbl)
      {
	  spatialite_e
	      ("AddFDOGeometryColumn() error: table '%s' does not exist\n",
	       table);
	  sqlite3_result_int (context, 0);
	  return;
      }
/* trying to add the column */
    xtable = gaiaDoubleQuotedSql (table);
    xcolumn = gaiaDoubleQuotedSql (column);
    sql_statement = sqlite3_mprintf ("ALTER TABLE \"%s\" "
				     "ADD COLUMN \"%s\" BLOB", xtable, xcolumn);
    free (xtable);
    free (xcolumn);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
/*ok, inserting into geometry_columns [FDO Spatial Metadata] */
    sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
				     "(f_table_name, f_geometry_column, geometry_type, "
				     "coord_dimension, srid, geometry_format) VALUES (%Q, %Q, %d, %d, %d, %Q)",
				     table, column, type, dimension,
				     (srid <= 0) ? -1 : srid, xformat);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("AddFDOGeometryColumn() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_RecoverFDOGeometryColumn (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ RecoverFDOGeometryColumn(table, column, srid, geometry_type , dimension, geometry_format )
/
/ checks if an existing TABLE.COLUMN satisfies the required geometric features
/ if yes adds it to FDO-styled SpatialMetaData 
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    const char *format;
    char xformat[64];
    int type;
    int srid = -1;
    int dimension = 2;
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    char **results;
    int rows;
    int columns;
    int i;
    int ok_tbl;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 3 [SRID] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    srid = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 4 [geometry_type] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    type = sqlite3_value_int (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 5 [dimension] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    dimension = sqlite3_value_int (argv[4]);
    if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 6 [geometry_format] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    format = (const char *) sqlite3_value_text (argv[5]);
    if (type ==
	GAIA_POINT
	|| type ==
	GAIA_LINESTRING
	|| type ==
	GAIA_POLYGON
	|| type ==
	GAIA_MULTIPOINT
	|| type ==
	GAIA_MULTILINESTRING
	|| type == GAIA_MULTIPOLYGON || type == GAIA_GEOMETRYCOLLECTION)
	;
    else
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 4 [geometry_type] has an illegal value\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (dimension < 2 || dimension > 4)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 5 [dimension] current version only accepts dimension=2,3,4\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (strcasecmp (format, "WKT") == 0)
	strcpy (xformat, "WKT");
    else if (strcasecmp (format, "WKB") == 0)
	strcpy (xformat, "WKB");
    else if (strcasecmp (format, "FGF") == 0)
	strcpy (xformat, "FGF");
    else if (strcasecmp (format, "SPATIALITE") == 0)
	strcpy (xformat, "SPATIALITE");
    else
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: argument 6 [geometry_format] has to be one of: WKT,WKB,FGF\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
/* checking if the table exists */
    sql_statement = sqlite3_mprintf ("SELECT name FROM sqlite_master "
				     "WHERE type = 'table' AND Upper(name) = Upper(%Q)",
				     table);
    ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows, &columns,
			     &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverFDOGeometryColumn: \"%s\"\n", errMsg);
	  sqlite3_free (errMsg);
	  return;
      }
    ok_tbl = 0;
    for (i = 1; i <= rows; i++)
	ok_tbl = 1;
    sqlite3_free_table (results);
    if (!ok_tbl)
      {
	  spatialite_e
	      ("RecoverFDOGeometryColumn() error: table '%s' does not exist\n",
	       table);
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (!recoverFDOGeomColumn
	(sqlite, (const unsigned char *) table,
	 (const unsigned char *) column, type, srid))
      {
	  spatialite_e ("RecoverFDOGeometryColumn(): validation failed\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    sql_statement = sqlite3_mprintf ("INSERT INTO geometry_columns "
				     "(f_table_name, f_geometry_column, geometry_type, "
				     "coord_dimension, srid, geometry_format) VALUES (%Q, %Q, %d, %d, %d, %Q)",
				     table, column, type, dimension,
				     (srid <= 0) ? -1 : srid, xformat);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("RecoverFDOGeometryColumn() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_DiscardFDOGeometryColumn (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ DiscardFDOGeometryColumn(table, column)
/
/ removes TABLE.COLUMN from the Spatial MetaData
/ returns 1 on success
/ 0 on failure
*/
    const unsigned char *table;
    const unsigned char *column;
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DiscardFDOGeometryColumn() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DiscardFDOGeometryColumn() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = sqlite3_value_text (argv[1]);
    sql_statement =
	sqlite3_mprintf
	("DELETE FROM geometry_columns WHERE Upper(f_table_name) = "
	 "Upper(%Q) AND Upper(f_geometry_column) = Upper(%Q)", table, column);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_result_int (context, 1);
    return;
  error:
    spatialite_e ("DiscardFDOGeometryColumn() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static int
eval_rtree_entry (int ok_geom, double geom_value, int ok_rtree,
		  double rtree_value)
{
/* evaluating geom-coord and rtree-coord */
    if (!ok_geom && !ok_rtree)
	return 1;
    if (ok_geom && ok_rtree)
      {
	  float g = (float) geom_value;
	  float r = (float) rtree_value;
	  double tic = fabs (geom_value - r) * 2.0;
	  float diff = g - r;
	  if (diff >= 1.5)
	      return 0;
	  if (diff > tic)
	      return 0;
	  return 1;
      }
    return 0;
}

static int
check_spatial_index (sqlite3 * sqlite, const unsigned char *table,
		     const unsigned char *geom)
{
/* attempting to check an R*Tree for consistency */
    char *xtable = NULL;
    char *xgeom = NULL;
    char *idx_name;
    char *xidx_name = NULL;
    char sql[1024];
    char *sql_statement;
    int ret;
    int is_defined = 0;
    sqlite3_stmt *stmt;
    sqlite3_int64 count_geom = 0;
    sqlite3_int64 count_rtree = 0;
    sqlite3_int64 count_rev = 0;
    double g_xmin = DBL_MAX;
    double g_ymin = DBL_MAX;
    double g_xmax = 0.0 - DBL_MAX;
    double g_ymax = 0.0 - DBL_MAX;
    int ok_g_xmin;
    int ok_g_ymin;
    int ok_g_xmax;
    int ok_g_ymax;
    double i_xmin = DBL_MAX;
    double i_ymin = DBL_MAX;
    double i_xmax = 0.0 - DBL_MAX;
    double i_ymax = 0.0 - DBL_MAX;
    int ok_i_xmin;
    int ok_i_ymin;
    int ok_i_xmax;
    int ok_i_ymax;
    int rowid_column = 0;
    int without_rowid = 0;

    if (is_without_rowid_table (sqlite, (char *) table))
      {
	  spatialite_e
	      ("check_spatial_index: table \"%s\" is WITHOUT ROWID\n", table);
	  without_rowid = 1;
	  goto err_label;
      }

/* checking if the R*Tree Spatial Index is defined */
    sql_statement = sqlite3_mprintf ("SELECT Count(*) FROM geometry_columns "
				     "WHERE Upper(f_table_name) = Upper(%Q) "
				     "AND Upper(f_geometry_column) = Upper(%Q) AND spatial_index_enabled = 1",
				     table, geom);
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  goto err_label;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	      is_defined = sqlite3_column_int (stmt, 0);
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto err_label;
	    }
      }
    sqlite3_finalize (stmt);
    if (!is_defined)
	goto err_label;

    xgeom = gaiaDoubleQuotedSql ((char *) geom);
    xtable = gaiaDoubleQuotedSql ((char *) table);
    idx_name = sqlite3_mprintf ("idx_%s_%s", table, geom);
    xidx_name = gaiaDoubleQuotedSql (idx_name);
    sqlite3_free (idx_name);

    if (!validateRowid (sqlite, (const char *) table))
      {
	  /* a physical column named "rowid" shadows the real ROWID */
	  rowid_column = 1;
	  goto err_label;
      }

/* counting how many Geometries are set into the main-table */
    sql_statement = sqlite3_mprintf ("SELECT Count(*) FROM \"%s\" "
				     "WHERE ST_GeometryType(\"%s\") IS NOT NULL",
				     xtable, xgeom);
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  goto err_label;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	      count_geom = sqlite3_column_int64 (stmt, 0);
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto err_label;
	    }
      }
    sqlite3_finalize (stmt);

/* counting how many R*Tree entries are defined */
    sql_statement = sqlite3_mprintf ("SELECT Count(*) FROM \"%s\"", xidx_name);
    ret =
	sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			    &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  goto err_label;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	      count_rtree = sqlite3_column_int64 (stmt, 0);
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto err_label;
	    }
      }
    sqlite3_finalize (stmt);
    if (count_geom != count_rtree)
      {
	  /* unexpected count difference */
	  goto mismatching_zero;
      }

/* now we'll check the R*Tree against the corresponding geometry-table */
    sql_statement =
	sqlite3_mprintf ("SELECT MbrMinX(g.\"%s\"), MbrMinY(g.\"%s\"), "
			 "MbrMaxX(g.\"%s\"), MbrMaxY(g.\"%s\"), i.xmin, i.ymin, i.xmax, i.ymax\n"
			 "FROM \"%s\" AS i\nLEFT JOIN \"%s\" AS g ON (g.ROWID = i.pkid)",
			 xgeom, xgeom, xgeom, xgeom, xidx_name, xtable);
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  goto err_label;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	    {
		/* checking a row */
		count_rev++;
		ok_g_xmin = 1;
		ok_g_ymin = 1;
		ok_g_xmax = 1;
		ok_g_ymax = 1;
		ok_i_xmin = 1;
		ok_i_ymin = 1;
		ok_i_xmax = 1;
		ok_i_ymax = 1;
		if (sqlite3_column_type (stmt, 0) == SQLITE_NULL)
		    ok_g_xmin = 0;
		else
		    g_xmin = sqlite3_column_double (stmt, 0);
		if (sqlite3_column_type (stmt, 1) == SQLITE_NULL)
		    ok_g_ymin = 0;
		else
		    g_ymin = sqlite3_column_double (stmt, 1);
		if (sqlite3_column_type (stmt, 2) == SQLITE_NULL)
		    ok_g_xmax = 0;
		else
		    g_xmax = sqlite3_column_double (stmt, 2);
		if (sqlite3_column_type (stmt, 3) == SQLITE_NULL)
		    ok_g_ymax = 0;
		else
		    g_ymax = sqlite3_column_double (stmt, 3);
		if (sqlite3_column_type (stmt, 4) == SQLITE_NULL)
		    ok_i_xmin = 0;
		else
		    i_xmin = sqlite3_column_double (stmt, 4);
		if (sqlite3_column_type (stmt, 5) == SQLITE_NULL)
		    ok_i_ymin = 0;
		else
		    i_ymin = sqlite3_column_double (stmt, 5);
		if (sqlite3_column_type (stmt, 6) == SQLITE_NULL)
		    ok_i_xmax = 0;
		else
		    i_xmax = sqlite3_column_double (stmt, 6);
		if (sqlite3_column_type (stmt, 7) == SQLITE_NULL)
		    ok_i_ymax = 0;
		else
		    i_ymax = sqlite3_column_double (stmt, 7);
		if (eval_rtree_entry (ok_g_xmin, g_xmin, ok_i_xmin, i_xmin)
		    == 0)
		    goto mismatching;
		if (eval_rtree_entry (ok_g_ymin, g_ymin, ok_i_ymin, i_ymin)
		    == 0)
		    goto mismatching;
		if (eval_rtree_entry (ok_g_xmax, g_xmax, ok_i_xmax, i_xmax)
		    == 0)
		    goto mismatching;
		if (eval_rtree_entry (ok_g_ymax, g_ymax, ok_i_ymax, i_ymax)
		    == 0)
		    goto mismatching;
	    }
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		goto err_label;
	    }
      }
    sqlite3_finalize (stmt);
    if (count_geom != count_rev)
	goto mismatching;
    strcpy (sql, "Check SpatialIndex: is valid");
    updateSpatiaLiteHistory (sqlite, (const char *) table,
			     (const char *) geom, sql);
    free (xgeom);
    free (xtable);
    free (xidx_name);
    return 1;
  mismatching:
    sqlite3_finalize (stmt);
    strcpy (sql, "Check SpatialIndex: INCONSISTENCIES detected");
    updateSpatiaLiteHistory (sqlite, (const char *) table,
			     (const char *) geom, sql);
  mismatching_zero:
    if (xgeom)
	free (xgeom);
    if (xtable)
	free (xtable);
    if (xidx_name)
	free (xidx_name);
    return 0;
  err_label:
    if (xgeom)
	free (xgeom);
    if (xtable)
	free (xtable);
    if (xidx_name)
	free (xidx_name);
    if (rowid_column)
	return -2;
    if (without_rowid)
	return -3;
    return -1;
}

static int
check_any_spatial_index (sqlite3 * sqlite)
{
/* attempting to check any defined R*Tree for consistency */
    const unsigned char *table;
    const unsigned char *column;
    int status;
    char sql[1024];
    int ret;
    int invalid_rtree = 0;
    sqlite3_stmt *stmt;

/* retrieving any defined R*Tree */
    strcpy (sql,
	    "SELECT f_table_name, f_geometry_column FROM geometry_columns ");
    strcat (sql, "WHERE spatial_index_enabled = 1");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  return -1;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	    {
		/* checking a single R*Tree */
		table = sqlite3_column_text (stmt, 0);
		column = sqlite3_column_text (stmt, 1);
		status = check_spatial_index (sqlite, table, column);
		if (status < 0)
		  {
		      sqlite3_finalize (stmt);
		      return status;
		  }
		if (status == 0)
		    invalid_rtree = 1;
	    }
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		return -1;
	    }
      }
    sqlite3_finalize (stmt);
    if (invalid_rtree)
	return 0;
    return 1;
}

static void
fnct_CheckSpatialIndex (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ CheckSpatialIndex()
/ CheckSpatialIndex(table, column)
/
/ checks a SpatialIndex for consistency, returning:
/ 1 - the R*Tree is fully consistent
/ 0 - the R*Tree is inconsistent
/ -1 if any physical "ROWID" column exist shadowing the real ROWID
/ NULL on failure
*/
    const unsigned char *table;
    const unsigned char *column;
    int status;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 0)
      {
	  /* no arguments: we must check any defined R*Tree */
	  status = check_any_spatial_index (sqlite);
	  if (status < 0)
	    {
		if (status == -2)
		    sqlite3_result_int (context, -1);
		else
		    sqlite3_result_null (context);
	    }
	  else if (status > 0)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }

    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CheckSpatialIndex() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CheckSpatialIndex() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    column = sqlite3_value_text (argv[1]);
    status = check_spatial_index (sqlite, table, column);
    if (status == -2)
	sqlite3_result_int (context, -1);
    else if (status == -3)
	sqlite3_result_int (context, -1);
    else if (status < 0)
	sqlite3_result_null (context);
    else if (status > 0)
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
}

static int
recover_spatial_index (sqlite3 * sqlite, const unsigned char *table,
		       const unsigned char *geom)
{
/* attempting to rebuild an R*Tree */
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    char *idx_name;
    char *xidx_name;
    char sql[1024];
    int is_defined = 0;
    sqlite3_stmt *stmt;
    int status;

/* checking if the R*Tree Spatial Index is defined */
    sql_statement = sqlite3_mprintf ("SELECT Count(*) FROM geometry_columns "
				     "WHERE Upper(f_table_name) = Upper(%Q) "
				     "AND Upper(f_geometry_column) = Upper(%Q) AND spatial_index_enabled = 1",
				     table, geom);
    ret = sqlite3_prepare_v2 (sqlite, sql_statement, strlen (sql_statement),
			      &stmt, NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  return -1;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	      is_defined = sqlite3_column_int (stmt, 0);
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		return -1;
	    }
      }
    sqlite3_finalize (stmt);
    if (!is_defined)
	return -1;

/* erasing the R*Tree table */
    idx_name = sqlite3_mprintf ("idx_%s_%s", table, geom);
    xidx_name = gaiaDoubleQuotedSql (idx_name);
    sqlite3_free (idx_name);
    sql_statement = sqlite3_mprintf ("DELETE FROM \"%s\"", xidx_name);
    free (xidx_name);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
/* populating the R*Tree table from scratch */
    status = buildSpatialIndexEx (sqlite, table, (const char *) geom);
    if (status == 0)
	;
    else
      {
	  if (status == -2)
	    {
		strcpy (sql,
			"SpatialIndex: a physical column named ROWID shadows the real ROWID");
		updateSpatiaLiteHistory (sqlite, (const char *) table,
					 (const char *) geom, sql);
	    }
	  else
	    {
		strcpy (sql, "SpatialIndex: unable to rebuild the R*Tree");
		updateSpatiaLiteHistory (sqlite, (const char *) table,
					 (const char *) geom, sql);
	    }
	  return status;
      }
    strcpy (sql, "SpatialIndex: successfully recovered");
    updateSpatiaLiteHistory (sqlite, (const char *) table,
			     (const char *) geom, sql);
    return 1;
  error:
    spatialite_e ("RecoverSpatialIndex() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    return 0;
}

static int
recover_any_spatial_index (sqlite3 * sqlite, int no_check)
{
/* attempting to rebuild any defined R*Tree */
    const unsigned char *table;
    const unsigned char *column;
    int status;
    char sql[1024];
    int ret;
    int to_be_fixed;
    int rowid_column = 0;
    int without_rowid = 0;
    sqlite3_stmt *stmt;

/* retrieving any defined R*Tree */
    strcpy (sql,
	    "SELECT f_table_name, f_geometry_column FROM geometry_columns ");
    strcat (sql, "WHERE spatial_index_enabled = 1");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("RecoverSpatialIndex SQL error: %s\n",
			sqlite3_errmsg (sqlite));
	  return -1;
      }
    while (1)
      {
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;
	  if (ret == SQLITE_ROW)
	    {
		/* checking a single R*Tree */
		table = sqlite3_column_text (stmt, 0);
		column = sqlite3_column_text (stmt, 1);
		to_be_fixed = 1;
		if (!no_check)
		  {
		      status = check_spatial_index (sqlite, table, column);
		      if (status < 0)
			{
			    /* some unexpected error occurred */
			    if (status == -2)
				rowid_column = 1;
			    if (status == -3)
				without_rowid = 1;
			    goto fatal_error;
			}
		      else if (status > 0)
			{
			    /* the Spatial Index is already valid */
			    to_be_fixed = 0;
			}
		  }
		if (to_be_fixed)
		  {
		      /* rebuilding the Spatial Index */
		      status = recover_spatial_index (sqlite, table, column);
		      if (status < 0)
			{
			    /* some unexpected error occurred */
			    if (status == -2)
				rowid_column = 1;
			    if (status == -3)
				without_rowid = 1;
			    goto fatal_error;
			}
		      else if (status == 0)
			  goto error;
		  }
	    }
	  else
	    {
		spatialite_e ("sqlite3_step() error: %s\n",
			      sqlite3_errmsg (sqlite));
		sqlite3_finalize (stmt);
		return -1;
	    }
      }
    sqlite3_finalize (stmt);
    return 1;
  error:
    sqlite3_finalize (stmt);
    return 0;
  fatal_error:
    sqlite3_finalize (stmt);
    if (rowid_column)
	return -2;
    if (without_rowid)
	return -3;
    return -1;
}

static void
fnct_RecoverSpatialIndex (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RecoverSpatialIndex()
/ RecoverSpatialIndex(no_check)
/ RecoverSpatialIndex(table, column)
/ RecoverSpatialIndex(table, column, no_check)
/
/ attempts to rebuild a SpatialIndex, returning:
/ 1 - on success
/ 0 - on failure
/ -1 if any physical "ROWID" column exist shadowing the real ROWID
/    or if the table was created WITHOUT ROWID
/ NULL if any syntax error is detected
*/
    const unsigned char *table;
    const unsigned char *column;
    int no_check = 0;
    int status;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc <= 1)
      {
	  /* no arguments: we must rebuild any defined R*Tree */
	  if (argc == 1)
	    {
		if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
		    no_check = sqlite3_value_int (argv[0]);
		else
		  {
		      spatialite_e
			  ("RecoverSpatialIndex() error: argument 1 [no_check] is not of the Integer type\n");
		      sqlite3_result_null (context);
		      return;
		  }
	    }
	  status = recover_any_spatial_index (sqlite, no_check);
	  if (status < 0)
	    {
		if (status == -2)
		    sqlite3_result_int (context, -1);
		else if (status == -3)
		    sqlite3_result_int (context, -1);
		else
		    sqlite3_result_null (context);
	    }
	  else if (status > 0)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }

    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverSpatialIndex() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    table = sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RecoverSpatialIndex() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    column = sqlite3_value_text (argv[1]);
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      no_check = sqlite3_value_int (argv[2]);
	  else
	    {
		spatialite_e
		    ("RecoverSpatialIndex() error: argument 2 [no_check] is not of the Integer type\n");
		sqlite3_result_null (context);
		return;
	    }
      }
    if (!no_check)
      {
	  /* checking the current SpatialIndex validity */
	  status = check_spatial_index (sqlite, table, column);
	  if (status < 0)
	    {
		/* some unexpected error occurred */
		if (status == -2 || status == -3)
		    sqlite3_result_int (context, -1);
		else
		    sqlite3_result_null (context);
		return;
	    }
	  else if (status > 0)
	    {
		/* the Spatial Index is already valid */
		sqlite3_result_int (context, 1);
		return;
	    }
      }
/* rebuilding the Spatial Index */
    status = recover_spatial_index (sqlite, table, column);
    if (status == -2)
	sqlite3_result_int (context, -1);
    else if (status < 0)
	sqlite3_result_null (context);
    else if (status > 0)
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
}

static void
fnct_CheckShadowedRowid (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ CheckShadowedRowid(table)
/
/ checks if some table has a "rowid" physical column shadowing the real ROWID
/ 1 - yes, the ROWID is shadowed
/ 0 - no, the ROWID isn't shadowed
/ NULL on failure (e.g. not existing table)
*/
    const unsigned char *table;
    int ret;
    char sql[128];
    sqlite3_stmt *stmt;
    int exists = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CheckShadowedRowid() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    table = sqlite3_value_text (argv[0]);

/* checking if the table exists */
    strcpy (sql,
	    "SELECT name FROM sqlite_master WHERE type = 'table' AND Lower(name) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckShadowedRowid: \"%s\"\n",
			sqlite3_errmsg (sqlite));
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, (const char *) table,
		       strlen ((const char *) table), SQLITE_STATIC);
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	      exists = 1;
      }
    sqlite3_finalize (stmt);
    if (!exists)
	sqlite3_result_null (context);
    else
      {
	  if (!validateRowid (sqlite, (const char *) table))
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
}

static void
fnct_CheckWithoutRowid (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ CheckWithoutRowid(table)
/
/ checks if some table has been created WITHOUT ROWID
/ 1 - yes, the table is WITHOUT ROWID
/ 0 - no, the ROWID should be supported
/ NULL on failure (e.g. not existing table)
*/
    const unsigned char *table;
    int ret;
    char sql[128];
    sqlite3_stmt *stmt;
    int exists = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CheckWithoutRowid() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_null (context);
	  return;
      }
    table = sqlite3_value_text (argv[0]);

/* checking if the table exists */
    strcpy (sql,
	    "SELECT name FROM sqlite_master WHERE type = 'table' AND Lower(name) = Lower(?)");
    ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
    if (ret != SQLITE_OK)
      {
	  spatialite_e ("CheckWithoutRowid: \"%s\"\n", sqlite3_errmsg (sqlite));
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_reset (stmt);
    sqlite3_clear_bindings (stmt);
    sqlite3_bind_text (stmt, 1, (const char *) table,
		       strlen ((const char *) table), SQLITE_STATIC);
    while (1)
      {
	  /* scrolling the result set rows */
	  ret = sqlite3_step (stmt);
	  if (ret == SQLITE_DONE)
	      break;		/* end of result set */
	  if (ret == SQLITE_ROW)
	      exists = 1;
      }
    sqlite3_finalize (stmt);
    if (!exists)
	sqlite3_result_null (context);
    else
      {
	  if (is_without_rowid_table (sqlite, (const char *) table))
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
}

static void
fnct_CreateSpatialIndex (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ CreateSpatialIndex(table, column )
/
/ creates a SpatialIndex based on Column and Table
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    char *sql_statement;
    char sql[1024];
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateSpatialIndex() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateSpatialIndex() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    if (is_without_rowid_table (sqlite, table))
      {
	  spatialite_e
	      ("CreateSpatialIndex() error: table '%s' is WITHOUT ROWID\n",
	       table);
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (!validateRowid (sqlite, table))
      {
	  spatialite_e
	      ("CreateSpatialIndex() error: a physical column named ROWID shadows the real ROWID\n");
	  sqlite3_result_int (context, -1);
	  return;
      }
    sql_statement =
	sqlite3_mprintf
	("UPDATE geometry_columns SET spatial_index_enabled = 1 "
	 "WHERE Upper(f_table_name) = Upper(%Q) AND "
	 "Upper(f_geometry_column) = Upper(%Q) AND spatial_index_enabled = 0",
	 table, column);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    if (sqlite3_changes (sqlite) == 0)
      {
	  spatialite_e
	      ("CreateSpatialIndex() error: either \"%s\".\"%s\" isn't a Geometry column or a SpatialIndex is already defined\n",
	       table, column);
	  sqlite3_result_int (context, 0);
	  return;
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    strcpy (sql, "R*Tree Spatial Index successfully created");
    updateSpatiaLiteHistory (sqlite, table, column, sql);
    return;
  error:
    spatialite_e ("CreateSpatialIndex() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CreateMbrCache (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CreateMbrCache(table, column )
/
/ creates an MBR Cache based on Column and Table
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    char *sql_statement;
    char sql[1024];
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateMbrCache() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("CreateMbrCache() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    sql_statement =
	sqlite3_mprintf
	("UPDATE geometry_columns SET spatial_index_enabled = 2 "
	 "WHERE Upper(f_table_name) = Upper(%Q) "
	 "AND Upper(f_geometry_column) = Upper(%Q) AND spatial_index_enabled = 0",
	 table, column);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    if (sqlite3_changes (sqlite) == 0)
      {
	  spatialite_e
	      ("CreateMbrCache() error: either \"%s\".\"%s\" isn't a Geometry column or a SpatialIndex is already defined\n",
	       table, column);
	  sqlite3_result_int (context, 0);
	  return;
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    strcpy (sql, "MbrCache successfully created");
    updateSpatiaLiteHistory (sqlite, table, column, sql);
    return;
  error:
    spatialite_e ("CreateMbrCache() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_DisableSpatialIndex (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ DisableSpatialIndex(table, column )
/
/ disables a SpatialIndex based on Column and Table
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    char sql[1024];
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DisableSpatialIndex() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("DisableSpatialIndex() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);

    sql_statement =
	sqlite3_mprintf
	("UPDATE geometry_columns SET spatial_index_enabled = 0 "
	 "WHERE Upper(f_table_name) = Upper(%Q) AND "
	 "Upper(f_geometry_column) = Upper(%Q) AND spatial_index_enabled <> 0",
	 table, column);
    ret = sqlite3_exec (sqlite, sql_statement, NULL, NULL, &errMsg);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    if (sqlite3_changes (sqlite) == 0)
      {
	  spatialite_e
	      ("DisableSpatialIndex() error: either \"%s\".\"%s\" isn't a Geometry column or no SpatialIndex is defined\n",
	       table, column);
	  sqlite3_result_int (context, 0);
	  return;
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    strcpy (sql, "SpatialIndex successfully disabled");
    updateSpatiaLiteHistory (sqlite, table, column, sql);
    return;
  error:
    spatialite_e ("DisableSpatialIndex() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_RebuildGeometryTriggers (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ RebuildGeometryTriggers(table, column )
/
/ rebuilds Geometry Triggers (constraints)  based on Column and Table
/ returns 1 on success
/ 0 on failure
*/
    const char *table;
    const char *column;
    char *sql_statement;
    char *errMsg = NULL;
    int ret;
    char **results;
    int rows;
    int columns;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RebuildGeometryTriggers() error: argument 1 [table_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("RebuildGeometryTriggers() error: argument 2 [column_name] is not of the String type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    column = (const char *) sqlite3_value_text (argv[1]);
    sql_statement =
	sqlite3_mprintf ("SELECT f_table_name FROM geometry_columns "
			 "WHERE Upper(f_table_name) = Upper(%Q) AND Upper(f_geometry_column) = Upper (%Q)",
			 table, column);
    ret = sqlite3_get_table (sqlite, sql_statement, &results, &rows, &columns,
			     NULL);
    sqlite3_free (sql_statement);
    if (ret != SQLITE_OK)
	goto error;
    sqlite3_free_table (results);
    if (rows <= 0)
      {
	  spatialite_e
	      ("RebuildGeometryTriggers() error: \"%s\".\"%s\" isn't a Geometry column\n",
	       table, column);
	  sqlite3_result_int (context, 0);
	  return;
      }
    updateGeometryTriggers (sqlite, table, column);
    sqlite3_result_int (context, 1);
    updateSpatiaLiteHistory (sqlite, table, column,
			     "Geometry Triggers successfully rebuilt");
    return;
  error:
    spatialite_e ("RebuildGeometryTriggers() error: \"%s\"\n", errMsg);
    sqlite3_free (errMsg);
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_UpdateLayerStatistics (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ UpdateLayerStatistics(table, column )
/
/ Updates LAYER_STATISTICS [based on Column and Table]
/ returns 1 on success
/ 0 on failure
*/
    const char *sql;
    const char *table = NULL;
    const char *column = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("UpdateLayerStatistics() error: argument 1 [table_name] is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  table = (const char *) sqlite3_value_text (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("UpdateLayerStatistics() error: argument 2 [column_name] is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  column = (const char *) sqlite3_value_text (argv[1]);
      }
    if (!update_layer_statistics (sqlite, table, column))
	goto error;
    sqlite3_result_int (context, 1);
    sql = "UpdateLayerStatistics";
    if (table == NULL)
	table = "ALL-TABLES";
    if (column == NULL)
	column = "ALL-GEOMETRY-COLUMNS";
    updateSpatiaLiteHistory (sqlite, (const char *) table,
			     (const char *) column, sql);
    return;
  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_UpgradeGeometryTriggers (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ UpgradeGeometryTriggers(transaction TRUE|FALSE)
/
/ Upgrades (reinstalls) all Geometry Triggers - requires a DB > 4.0.0
/ returns 1 on success
/ 0 on failure (NULL on invalid args)
*/
    char *errMsg = NULL;
    int ret;
    int transaction = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("UpgradeGeometryTriggers() error: argument 1 [TRANSACTION] is not of the Integer type\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (checkSpatialMetaData (sqlite) < 3)
      {
	  spatialite_e
	      ("UpgradeGeometryTriggers() error: invalid DB Layout (< v.4.0.0)\n");
	  sqlite3_result_int (context, 0);
	  return;
      }
    transaction = sqlite3_value_int (argv[0]);
    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    if (!upgradeGeometryTriggers (sqlite))
	goto error;
    if (transaction)
      {
	  /* committing the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    updateSpatiaLiteHistory (sqlite, "ALL-TABLES", NULL,
			     "Upgraded Geometry Triggers");
    sqlite3_result_int (context, 1);
    return;

  error:
    if (transaction)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      sqlite3_free (errMsg);
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_GetLayerExtent (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GetLayerExtent(table)
/ GetLayerExtent(table, column )
/ GetLayerExtent(table, column, pessimistic )
/
/ Return a Geometry (Envelope) corresponding to the full layer
/ extent [eventually updating the supporting statistics
/ NULL on failure
*/
    const char *table = NULL;
    const char *column = NULL;
    int pessimistic = 0;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("GetLayerExtent() error: argument 1 [table_name] is not of the String type\n");
		sqlite3_result_null (context);
		return;
	    }
	  table = (const char *) sqlite3_value_text (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("GetLayerExtent() error: argument 2 [column_name] is not of the String type\n");
		sqlite3_result_null (context);
		return;
	    }
	  column = (const char *) sqlite3_value_text (argv[1]);
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		spatialite_e
		    ("GetLayerExtent() error: argument 3 [OPTIMISTIC/PESSIMISTIC] is not of the Integer type\n");
		sqlite3_result_null (context);
		return;
	    }
	  pessimistic = sqlite3_value_int (argv[2]);
      }
    geom = gaiaGetLayerExtent (sqlite, table, column, pessimistic);
    if (!geom)
	goto error;
/* builds the BLOB geometry to be returned */
    gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode, tiny_point);
    sqlite3_result_blob (context, p_result, len, free);
    gaiaFreeGeomColl (geom);
    return;
  error:
    sqlite3_result_null (context);
    return;
}

static void
fnct_InvalidateLayerStatistics (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ InvalidateLayerStatistics(void)
/ InvalidateLayerStatistics(table)
/ InvalidateLayerStatistics(table, column )
/
/ Immediately and unconditionally invalidates Layer Statistics
/ returns 1 on success
/ 0 on failure
*/
    const char *sql;
    const char *table = NULL;
    const char *column = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("InvalidateLayerStatistics() error: argument 1 [table_name] is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  table = (const char *) sqlite3_value_text (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	    {
		spatialite_e
		    ("InvalidateLayerStatistics() error: argument 2 [column_name] is not of the String type\n");
		sqlite3_result_int (context, 0);
		return;
	    }
	  column = (const char *) sqlite3_value_text (argv[1]);
      }
    if (!gaiaStatisticsInvalidate (sqlite, table, column))
	goto error;
    sqlite3_result_int (context, 1);
    sql = "InvalidateLayerStatistics";
    if (table == NULL)
	table = "ALL-TABLES";
    if (column == NULL)
	column = "ALL-GEOMETRY-COLUMNS";
    updateSpatiaLiteHistory (sqlite, (const char *) table,
			     (const char *) column, sql);
    return;
  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CreateRasterCoveragesTable (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ CreateRasterCoveragesTable()
/
/ creates the main RasterCoverages table 
/ returns 1 on success
/ 0 on failure
*/
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (!createRasterCoveragesTable (sqlite))
	goto error;
    updateSpatiaLiteHistory (sqlite, "*** Raster Coverages ***", NULL,
			     "Main table successfully created");
    sqlite3_result_int (context, 1);
    return;

  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CreateVectorCoveragesTables (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ CreateVectorCoveragesTables()
/
/ creates the main VectorCoverages table 
/ returns 1 on success
/ 0 on failure
*/
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (!createVectorCoveragesTable (sqlite))
	goto error;
    updateSpatiaLiteHistory (sqlite, "*** Vector Coverages ***", NULL,
			     "Main table successfully created");
    sqlite3_result_int (context, 1);
    return;

  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_CreateWMSTables (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ WMS_CreateTables()
/
/ creates the WMS support tables
/ returns 1 on success
/ 0 on failure
*/
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (!createWMSTables (sqlite))
	goto error;
    updateSpatiaLiteHistory (sqlite, "*** WMS ***", NULL,
			     "Support tables successfully created");
    sqlite3_result_int (context, 1);
    return;

  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_RegisterWMSGetCapabilities (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ WMS_RegisterGetCapabilities(Text url)
/   or
/ WMS_RegisterGetCapabilities(Text url, Text title,
/                        Text abstract)
/
/ inserts a WMS GetCapabilities
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *title = NULL;
    const char *abstract = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[2]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[1]);
	  abstract = (const char *) sqlite3_value_text (argv[2]);
      }
    ret = register_wms_getcapabilities (sqlite, url, title, abstract);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterWMSGetCapabilities (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ WMS_UnRegisterGetCapabilities(Text url)
/
/ deletes a WMS GetCapabilities (and all related children)
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    ret = unregister_wms_getcapabilities (sqlite, url);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetWMSGetCapabilitiesInfos (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ WMS_SetGetCapabilitiesInfos(Text url, Text title,
/                        Text abstract)
/
/ updates the descriptive infos supporting a WMS GetCapabilities
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *title = NULL;
    const char *abstract = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    abstract = (const char *) sqlite3_value_text (argv[2]);
    ret = set_wms_getcapabilities_infos (sqlite, url, title, abstract);
    sqlite3_result_int (context, ret);
}

static int
validate_wms_bgcolor (const char *bgcolor)
{
/* testing for a valid HexRGB color value */
    const char *p = bgcolor;
    int len = strlen (bgcolor);
    if (len != 6)
	return 0;
    while (*p != '\0')
      {
	  int ok = 0;
	  if (*p >= 'a' && *p <= 'f')
	      ok = 1;
	  if (*p >= 'A' && *p <= 'F')
	      ok = 1;
	  if (*p >= '0' && *p <= '9')
	      ok = 1;
	  if (!ok)
	      return 0;
	  p++;
      }
    return 1;
}

static void
fnct_RegisterWMSGetMap (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ WMS_RegisterGetMap(Text getcapabilitites_url, Text getmap_url,
/                    Text layer_name, Text version, Text ref_sys,
/                    Text image_format, Text style, Int transparent,
/                    Int flip_axes)
/   or
/ WMS_RegisterGetMap(Text getcapabilitites_url, Text getmap_url,
/                    Text layer_name, Text version, Text ref_sys,
/                    Text image_format, Text style, Int transparent,
/                    Int flip_axes,  Int tiled, Int cached, 
/                    Int tile_width, Int tile_height)
/   or
/ WMS_RegisterGetMap(Text getcapabilitites_url, Text getmap_url,
/                    Text layer_name, Text title, Text abstract,
/                    Text version, Text ref_sys, Text image_format,
/                    Text style, Int transparent, Int flip_axes,
/                    Int tiled, Int cached, Int tile_width, 
/                    Int tile_height, Text bgcolor, Int is_queryable,
/                    Text getfeatureinfo_url)
/
/ inserts a WMS GetMap
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *getcapabilities_url;
    const char *getmap_url;
    const char *layer_name;
    const char *title = NULL;
    const char *abstract = NULL;
    const char *version;
    const char *ref_sys;
    const char *image_format;
    const char *style;
    int transparent;
    int flip_axes;
    int tiled = 0;
    int cached = 0;
    int tile_width = 512;
    int tile_height = 512;
    const char *bgcolor = NULL;
    int is_queryable = 0;
    const char *getfeatureinfo_url = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    getcapabilities_url = (const char *) sqlite3_value_text (argv[0]);
    getmap_url = (const char *) sqlite3_value_text (argv[1]);
    layer_name = (const char *) sqlite3_value_text (argv[2]);
    if (argc == 9 || argc == 13)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[4]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[5]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[6]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  version = (const char *) sqlite3_value_text (argv[3]);
	  ref_sys = (const char *) sqlite3_value_text (argv[4]);
	  image_format = (const char *) sqlite3_value_text (argv[5]);
	  style = (const char *) sqlite3_value_text (argv[6]);
	  if (sqlite3_value_type (argv[7]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[8]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  transparent = sqlite3_value_int (argv[7]);
	  flip_axes = sqlite3_value_int (argv[8]);
      }
    if (argc == 13)
      {
	  if (sqlite3_value_type (argv[9]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[10]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[11]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[12]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  tiled = sqlite3_value_int (argv[9]);
	  cached = sqlite3_value_int (argv[10]);
	  tile_width = sqlite3_value_int (argv[11]);
	  tile_height = sqlite3_value_int (argv[12]);
      }
    if (argc == 18)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[1]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[2]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[3]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[4]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[5]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[6]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[7]) != SQLITE_TEXT ||
	      sqlite3_value_type (argv[8]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  getcapabilities_url = (const char *) sqlite3_value_text (argv[0]);
	  getmap_url = (const char *) sqlite3_value_text (argv[1]);
	  layer_name = (const char *) sqlite3_value_text (argv[2]);
	  title = (const char *) sqlite3_value_text (argv[3]);
	  abstract = (const char *) sqlite3_value_text (argv[4]);
	  version = (const char *) sqlite3_value_text (argv[5]);
	  ref_sys = (const char *) sqlite3_value_text (argv[6]);
	  image_format = (const char *) sqlite3_value_text (argv[7]);
	  style = (const char *) sqlite3_value_text (argv[8]);
	  if (sqlite3_value_type (argv[9]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[10]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[11]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[12]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[13]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[14]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[16]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  transparent = sqlite3_value_int (argv[9]);
	  flip_axes = sqlite3_value_int (argv[10]);
	  tiled = sqlite3_value_int (argv[11]);
	  cached = sqlite3_value_int (argv[12]);
	  tile_width = sqlite3_value_int (argv[13]);
	  tile_height = sqlite3_value_int (argv[14]);
	  is_queryable = sqlite3_value_int (argv[16]);
	  if (sqlite3_value_type (argv[15]) == SQLITE_NULL)
	      bgcolor = NULL;
	  else if (sqlite3_value_type (argv[15]) == SQLITE_TEXT)
	    {
		bgcolor = (const char *) sqlite3_value_text (argv[15]);
		if (!validate_wms_bgcolor (bgcolor))
		  {
		      sqlite3_result_int (context, -1);
		      return;
		  }
	    }
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  if (sqlite3_value_type (argv[17]) == SQLITE_NULL)
	      getfeatureinfo_url = NULL;
	  else if (sqlite3_value_type (argv[17]) == SQLITE_TEXT)
	      getfeatureinfo_url = (const char *) sqlite3_value_text (argv[17]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    ret =
	register_wms_getmap (sqlite, getcapabilities_url, getmap_url,
			     layer_name, title, abstract, version, ref_sys,
			     image_format, style, transparent, flip_axes,
			     tiled, cached, tile_width, tile_height, bgcolor,
			     is_queryable, getfeatureinfo_url);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterWMSGetMap (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ WMS_UnRegisterGetMap(Text url, Text layer_name)
/
/ deletes a WMS GetMap (and all related children)
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    ret = unregister_wms_getmap (sqlite, url, layer_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetWMSGetMapInfos (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ WMS_SetGetMapInfos(Text url, Text layer_name, Text title, Text abstract)
/
/ updates the descriptive infos supporting a WMS GetMap
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *title;
    const char *abstract;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    title = (const char *) sqlite3_value_text (argv[2]);
    abstract = (const char *) sqlite3_value_text (argv[3]);
    ret = set_wms_getmap_infos (sqlite, url, layer_name, title, abstract);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetWMSGetMapCopyright (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ WMS_SetGetMapCopyright(Text url, Text layer_name, Text copyright)
/    or
/ WMS_SetGetMapCopyright(Text url, Text layer_name, Text copyright,
/                        Text license)
/
/ updates copyright infos supporting a WMS GetMap
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *copyright = NULL;
    const char *license = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	copyright = (const char *) sqlite3_value_text (argv[2]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	      license = (const char *) sqlite3_value_text (argv[3]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    ret =
	set_wms_getmap_copyright (sqlite, url, layer_name, copyright, license);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetWMSGetMapOptions (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ WMS_SetGetMapOptions(Text url, Text layer_name, Int transparent,
/                      Int flip_axes)
/   or
/ WMS_SetGetMapOptions(Text url, Text layer_name, Int tiled, Int cached,
/                      Int tile_width, Int tile_height)
/   or
/ WMS_SetGetMapOptions(Text url, Text layer_name, Int is_queryable,
/                      Text getfeatureinfo_url)
/   or
/ WMS_SetGetMapOptions(Text url, Text layer_name, Text bgcolor)
/
/ updates the options supporting a WMS GetMap
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    int transparent;
    int flip_axes;
    int is_queryable;
    int tiled;
    int cached;
    int tile_width = 512;
    int tile_height = 512;
    const char *getfeatureinfo_url = NULL;
    const char *bgcolor = NULL;
    char mode = '\0';
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT ||
	sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	    {
		mode = 'B';
		bgcolor = (const char *) sqlite3_value_text (argv[2]);
		if (!validate_wms_bgcolor (bgcolor))
		  {
		      sqlite3_result_int (context, -1);
		      return;
		  }
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
	    {
		mode = 'B';
		bgcolor = NULL;
	    }
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER
	      && sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		mode = 'F';
		transparent = sqlite3_value_int (argv[2]);
		flip_axes = sqlite3_value_int (argv[3]);
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER
		   && sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	    {
		mode = 'Q';
		is_queryable = sqlite3_value_int (argv[2]);
		getfeatureinfo_url =
		    (const char *) sqlite3_value_text (argv[3]);
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER
		   && sqlite3_value_type (argv[3]) == SQLITE_NULL)
	    {
		mode = 'Q';
		is_queryable = sqlite3_value_int (argv[2]);
		getfeatureinfo_url = NULL;
	    }
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    if (argc == 6)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[3]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[4]) != SQLITE_INTEGER ||
	      sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  mode = 'T';
	  tiled = sqlite3_value_int (argv[2]);
	  cached = sqlite3_value_int (argv[3]);
	  tile_width = sqlite3_value_int (argv[4]);
	  tile_height = sqlite3_value_int (argv[5]);
      }
    switch (mode)
      {
      case 'B':
	  ret = set_wms_getmap_bgcolor (sqlite, url, layer_name, bgcolor);
	  break;
      case 'F':
	  ret =
	      set_wms_getmap_options (sqlite, url, layer_name, transparent,
				      flip_axes);
	  break;
      case 'Q':
	  ret =
	      set_wms_getmap_queryable (sqlite, url, layer_name, is_queryable,
					getfeatureinfo_url);
	  break;
      case 'T':
	  ret =
	      set_wms_getmap_tiled (sqlite, url, layer_name, tiled, cached,
				    tile_width, tile_height);
	  break;
      default:
	  ret = -1;
      };
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterWMSSetting (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ WMS_RegisterSetting(Text url, Text layer_name, Text key, Text value)
/   or
/ WMS_RegisterSetting(Text url, Text layer_name, Text key, Text value,
/                     Int default)
/
/ inserts a WMS GetMap Setting
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *key;
    const char *value;
    int is_default = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    key = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    value = (const char *) sqlite3_value_text (argv[3]);
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_default = sqlite3_value_int (argv[4]);
      }
    ret =
	register_wms_setting (sqlite, url, layer_name, key, value, is_default);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterWMSSetting (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ WMS_UnRegisterSetting(Text url, Text layer_name, Text key, Text value)
/
/ deletes a WMS GetMap Setting
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *key;
    const char *value;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    key = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    value = (const char *) sqlite3_value_text (argv[3]);
    ret = unregister_wms_setting (sqlite, url, layer_name, key, value);
    sqlite3_result_int (context, ret);
}

static void
fnct_DefaultWMSSetting (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ WMS_DefaultSetting(Text url, Text layer_name, Text key, Text value)
/
/ updates some GetMap default setting
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *key;
    const char *value;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    key = (const char *) sqlite3_value_text (argv[2]);
    value = (const char *) sqlite3_value_text (argv[3]);
    ret = set_wms_default_setting (sqlite, url, layer_name, key, value);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterWMSRefSys (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ WMS_RegisterRefSys(Text url, Text layer_name, Text ref_sys, Double minx,
/                    Double miny, Double maxx, Double maxy)
/   or
/ WMS_RegisterRefSys(Text url, Text layer_name, Text ref_sys, Double minx,
/                    Double miny, Double maxx, Double maxy, Int default)
/
/ inserts a WMS GetMap SRS
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *ref_sys;
    double minx;
    double miny;
    double maxx;
    double maxy;
    int is_default = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ref_sys = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[3]);
	  minx = val;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	minx = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[4]);
	  miny = val;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	miny = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[5]);
	  maxx = val;
      }
    else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	maxx = sqlite3_value_double (argv[5]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[6]);
	  maxy = val;
      }
    else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	maxy = sqlite3_value_double (argv[6]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 8)
      {
	  if (sqlite3_value_type (argv[7]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_default = sqlite3_value_int (argv[7]);
      }
    ret =
	register_wms_srs (sqlite, url, layer_name, ref_sys, minx, miny, maxx,
			  maxy, is_default);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterWMSRefSys (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ WMS_UnRegisterRefSys(Text url, Text layer_name, Text ref_sys)
/
/ deletes a WMS GetMap SRS
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *ref_sys;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ref_sys = (const char *) sqlite3_value_text (argv[2]);
    ret = unregister_wms_srs (sqlite, url, layer_name, ref_sys);
    sqlite3_result_int (context, ret);
}

static void
fnct_DefaultWMSRefSys (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ WMS_DefaultRefSys(Text url, Text layer_name, Text ref_sys)
/
/ updates some GetMap default SRS
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *url;
    const char *layer_name;
    const char *ref_sys;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    url = (const char *) sqlite3_value_text (argv[0]);
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    ref_sys = (const char *) sqlite3_value_text (argv[2]);
    ret = set_wms_default_srs (sqlite, url, layer_name, ref_sys);
    sqlite3_result_int (context, ret);
}

static void
fnct_WMSGetMapRequestURL (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ WMS_GetMapRequestURL(Text getmap_url, Text layer_name, Int width,
/                      Int height, Double minx, Double miny,
/                      Double maxx, Double maxy)
/
/ returns a WMS GetMap request URL on success
/ NULL on invalid arguments
*/
    char *url;
    const char *getmap_url;
    const char *layer_name;
    int width;
    int height;
    double minx;
    double miny;
    double maxx;
    double maxy;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    getmap_url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    width = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    height = sqlite3_value_int (argv[3]);
    if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	minx = sqlite3_value_double (argv[4]);
    else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[4]);
	  minx = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	miny = sqlite3_value_double (argv[5]);
    else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[5]);
	  miny = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	maxx = sqlite3_value_double (argv[6]);
    else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[6]);
	  maxx = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	maxy = sqlite3_value_double (argv[7]);
    else if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[7]);
	  maxy = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    url =
	wms_getmap_request_url (sqlite, getmap_url, layer_name, width, height,
				minx, miny, maxx, maxy);
    if (url == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, url, strlen (url), sqlite3_free);
}

static void
fnct_WMSGetFeatureInfoRequestURL (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ WMS_GetFeatureInfoRequestURL(Text getmap_url, Text layer_name, Int width,
/                              Int height, int x, int y, Double minx,
/                              Double miny, Double maxx, Double maxy)
/   or
/ WMS_GetFeatureInfoRequestURL(Text getmap_url, Text layer_name, Int width,
/                              Int height, int x, int y, Double minx,
/                              Double miny, Double maxx, Double maxy,
/                              Int feature_count )
/
/ returns a WMS GetFeatureInfo request URL on success
/ NULL on invalid arguments
*/
    char *url;
    const char *getmap_url;
    const char *layer_name;
    int width;
    int height;
    int x;
    int y;
    double minx;
    double miny;
    double maxx;
    double maxy;
    int feature_count = 1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    getmap_url = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    layer_name = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    width = sqlite3_value_int (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    height = sqlite3_value_int (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = sqlite3_value_int (argv[4]);
    if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    y = sqlite3_value_int (argv[5]);
    if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	minx = sqlite3_value_double (argv[6]);
    else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[6]);
	  minx = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	miny = sqlite3_value_double (argv[7]);
    else if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[7]);
	  miny = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[8]) == SQLITE_FLOAT)
	maxx = sqlite3_value_double (argv[8]);
    else if (sqlite3_value_type (argv[8]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[8]);
	  maxx = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[9]) == SQLITE_FLOAT)
	maxy = sqlite3_value_double (argv[9]);
    else if (sqlite3_value_type (argv[9]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[9]);
	  maxy = val;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 11)
      {
	  if (sqlite3_value_type (argv[10]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  feature_count = sqlite3_value_int (argv[10]);
      }
    url =
	wms_getfeatureinfo_request_url (sqlite, getmap_url, layer_name, width,
					height, x, y, minx, miny, maxx, maxy,
					feature_count);
    if (url == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, url, strlen (url), sqlite3_free);
}

static void
fnct_RegisterDataLicense (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RegisterDataLicense(Text license_name)
/    or
/ RegisterDataLicense(Text license_name, Text license_url)
/
/ inserts a Data License
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *license_name;
    const char *url = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    license_name = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	      url = (const char *) sqlite3_value_text (argv[1]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    ret = register_data_license (sqlite, license_name, url);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterDataLicense (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterDataLicense(Text license_name)
/
/ deletes a Data License
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *license_name;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    license_name = (const char *) sqlite3_value_text (argv[0]);
    ret = unregister_data_license (sqlite, license_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_RenameDataLicense (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ RenameDataLicense(Text old_name, Text new_name)
/
/ renames an existing Data License
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *old_name;
    const char *new_name;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT ||
	sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    old_name = (const char *) sqlite3_value_text (argv[0]);
    new_name = (const char *) sqlite3_value_text (argv[1]);
    ret = rename_data_license (sqlite, old_name, new_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetDataLicenseUrl (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ SetDataLicenseUrl(Text license_name, Text license_url)
/
/ updates a Data License URL
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *license_name;
    const char *url;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT ||
	sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    license_name = (const char *) sqlite3_value_text (argv[0]);
    url = (const char *) sqlite3_value_text (argv[1]);
    ret = set_data_license_url (sqlite, license_name, url);
    sqlite3_result_int (context, ret);
}

static void
fnct_CreateMetaCatalogTables (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ CreateMetaCatalogTables(transaction TRUE|FALSE)
/
/ creates (or re-creates) both "splite_metacatalog"
/ and "splite_metacatalog_statistics" tables
/ returns 1 on success
/ 0 on failure
*/
    char *errMsg = NULL;
    int ret;
    int transaction = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("CreateMetaCatalogTables() error: argument 1 [TRANSACTION] is not of the Integer type\n");
	  sqlite3_result_null (context);
	  return;
      }
    transaction = sqlite3_value_int (argv[0]);
    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    if (!gaiaCreateMetaCatalogTables (sqlite))
	goto error;
    if (transaction)
      {
	  /* committing the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    updateSpatiaLiteHistory (sqlite, "*** MetaCatalog ***", NULL,
			     "Tables successfully created and initialized");
    sqlite3_result_int (context, 1);
    return;

  error:
    if (transaction)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      sqlite3_free (errMsg);
      }
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_UpdateMetaCatalogStatistics (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ UpdateMetaCatalogStatistics(transaction TRUE|FALSE, table, column)
/ UpdateMetaCatalogStatistics(transaction TRUE|FALSE, master_table, table_name, column_name)
/
/ updates the MetaCatalog statistics
/ returns 1 on success
/ 0 on failure
*/
    char *errMsg = NULL;
    int ret;
    int transaction = 0;
    const char *master_table = NULL;
    const char *table = NULL;
    const char *column = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("UpdateMetaCatalogStatistics() error: argument 1 [TRANSACTION] is not of the Integer type\n");
	  sqlite3_result_null (context);
	  return;
      }
    transaction = sqlite3_value_int (argv[0]);
    if (argc == 3)
      {
	  /* table & column mode */
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	      table = (const char *) sqlite3_value_text (argv[1]);
	  else
	    {
		spatialite_e
		    ("UpdateMetaCatalogStatistics() error: argument 2 [TABLE_NAME] is not of the Text type\n");
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      column = (const char *) sqlite3_value_text (argv[2]);
	  else
	    {
		spatialite_e
		    ("UpdateMetaCatalogStatistics() error: argument 2 [COLUMN_NAME] is not of the Text type\n");
		sqlite3_result_null (context);
		return;
	    }
      }
    else
      {
	  /* master-table & table_name & column_name mode */
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	      master_table = (const char *) sqlite3_value_text (argv[1]);
	  else
	    {
		spatialite_e
		    ("UpdateMetaCatalogStatistics() error: argument 2 [MASTER_TABLE] is not of the Text type\n");
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      table = (const char *) sqlite3_value_text (argv[2]);
	  else
	    {
		spatialite_e
		    ("UpdateMetaCatalogStatistics() error: argument 3 [TABLE_NAME] is not of the Text type\n");
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	      column = (const char *) sqlite3_value_text (argv[3]);
	  else
	    {
		spatialite_e
		    ("UpdateMetaCatalogStatistics() error: argument 3 [COLUMN_NAME] is not of the Text type\n");
		sqlite3_result_null (context);
		return;
	    }
      }
    if (transaction)
      {
	  /* starting a Transaction */
	  ret = sqlite3_exec (sqlite, "BEGIN", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    if (master_table != NULL)
      {
	  if (!gaiaUpdateMetaCatalogStatisticsFromMaster
	      (sqlite, master_table, table, column))
	      goto error;
      }
    else
      {
	  if (!gaiaUpdateMetaCatalogStatistics (sqlite, table, column))
	      goto error;
      }
    if (transaction)
      {
	  /* committing the still pending Transaction */
	  ret = sqlite3_exec (sqlite, "COMMIT", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      goto error;
      }
    updateSpatiaLiteHistory (sqlite, "*** MetaCatalog ***", NULL,
			     "Statistics successfully updated");
    sqlite3_result_int (context, 1);
    return;

  error:
    if (transaction)
      {
	  /* performing a Rollback */
	  ret = sqlite3_exec (sqlite, "ROLLBACK", NULL, NULL, &errMsg);
	  if (ret != SQLITE_OK)
	      sqlite3_free (errMsg);
      }
    sqlite3_result_int (context, 0);
    return;
}

static gaiaPointPtr
simplePoint (gaiaGeomCollPtr geo)
{
/* helper function
/ if this GEOMETRY contains only one POINT, and no other elementary geometry
/ the POINT address will be returned
/ otherwise NULL will be returned
*/
    int cnt = 0;
    gaiaPointPtr point;
    gaiaPointPtr this_point = NULL;
    if (!geo)
	return NULL;
    if (geo->FirstLinestring || geo->FirstPolygon)
	return NULL;
    point = geo->FirstPoint;
    while (point)
      {
	  /* counting how many POINTs are there */
	  cnt++;
	  this_point = point;
	  point = point->Next;
      }
    if (cnt == 1 && this_point)
	return this_point;
    return NULL;
}

static gaiaLinestringPtr
simpleLinestring (gaiaGeomCollPtr geo)
{
/* helper function
/ if this GEOMETRY contains only one LINESTRING, and no other elementary geometry
/ the LINESTRING address will be returned
/ otherwise NULL will be returned
*/
    int cnt = 0;
    gaiaLinestringPtr line;
    gaiaLinestringPtr this_line = NULL;
    if (!geo)
	return NULL;
    if (geo->FirstPoint || geo->FirstPolygon)
	return NULL;
    line = geo->FirstLinestring;
    while (line)
      {
	  /* counting how many LINESTRINGs are there */
	  cnt++;
	  this_line = line;
	  line = line->Next;
      }
    if (cnt == 1 && this_line)
	return this_line;
    return NULL;
}

static gaiaPolygonPtr
simplePolygon (gaiaGeomCollPtr geo)
{
/* helper function
/ if this GEOMETRY contains only one POLYGON, and no other elementary geometry
/ the POLYGON address will be returned
/ otherwise NULL will be returned
*/
    int cnt = 0;
    gaiaPolygonPtr polyg;
    gaiaPolygonPtr this_polyg = NULL;
    if (!geo)
	return NULL;
    if (geo->FirstPoint || geo->FirstLinestring)
	return NULL;
    polyg = geo->FirstPolygon;
    while (polyg)
      {
	  /* counting how many POLYGONs are there */
	  cnt++;
	  this_polyg = polyg;
	  polyg = polyg->Next;
      }
    if (cnt == 1 && this_polyg)
	return this_polyg;
    return NULL;
}

static void
fnct_AsText (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsText(BLOB encoded geometry)
/
/ returns the corresponding WKT encoded value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int decimal_precision = -1;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  decimal_precision = cache->decimal_precision;
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    gaiaOutBufferInitialize (&out_buf);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (decimal_precision >= 0)
	      gaiaOutWktEx (&out_buf, geo, decimal_precision);
	  else
	      gaiaOutWkt (&out_buf, geo);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsWkt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsWkt(BLOB encoded geometry [, Integer precision])
/
/ returns the corresponding WKT encoded value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int precision = 15;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    gaiaOutBufferInitialize (&out_buf);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaOutWktStrict (&out_buf, geo, precision);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

/*
/
/ AsSvg(geometry,[relative], [precision]) implementation
/
////////////////////////////////////////////////////////////
/
/ Author: Klaus Foerster klaus.foerster@svg.cc
/ version 0.9. 2008 September 21
 /
 */

static void
fnct_AsSvg (sqlite3_context * context, int argc, sqlite3_value ** argv,
	    int relative, int precision)
{
/* SQL function:
   AsSvg(BLOB encoded geometry, [int relative], [int precision])
   returns the corresponding SVG encoded value or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  /* make sure relative is 0 or 1 */
	  if (relative > 0)
	      relative = 1;
	  else
	      relative = 0;
	  /* make sure precision is between 0 and 15 - default to 6 if absent */
	  if (precision > GAIA_SVG_DEFAULT_MAX_PRECISION)
	      precision = GAIA_SVG_DEFAULT_MAX_PRECISION;
	  if (precision < 0)
	      precision = 0;
	  /* produce SVG-notation - actual work is done in gaiageo/gg_wkt.c */
	  gaiaOutBufferInitialize (&out_buf);
	  gaiaOutSvg (&out_buf, geo, relative, precision);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsSvg1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* called without additional arguments */
    fnct_AsSvg (context, argc, argv, GAIA_SVG_DEFAULT_RELATIVE,
		GAIA_SVG_DEFAULT_PRECISION);
}

static void
fnct_AsSvg2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* called with relative-switch */
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	fnct_AsSvg (context, argc, argv, sqlite3_value_int (argv[1]),
		    GAIA_SVG_DEFAULT_PRECISION);
    else
	sqlite3_result_null (context);
}

static void
fnct_AsSvg3 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* called with relative-switch and precision-argument */
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER
	&& sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	fnct_AsSvg (context, argc, argv, sqlite3_value_int (argv[1]),
		    sqlite3_value_int (argv[2]));
    else
	sqlite3_result_null (context);
}

/* END of Klaus Foerster AsSvg() implementation */


#ifndef OMIT_PROJ		/* PROJ.4 is strictly required to support KML */
static void
fnct_AsKml1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsKml(BLOB encoded geometry [, Integer precision])
/
/ returns the corresponding 'bare geom' KML representation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geo_wgs84;
    char *proj_from;
    char *proj_to;
    int precision = 15;
    void *data = sqlite3_user_data (context);
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    gaiaOutBufferInitialize (&out_buf);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (geo->Srid == 4326)
	      ;			/* already WGS84 */
	  else if (geo->Srid <= 0)
	    {
		/* unknown SRID: giving up */
		sqlite3_result_null (context);
		goto stop;
	    }
	  else
	    {
		/* attempting to reproject into WGS84 */
		getProjParams (sqlite, geo->Srid, &proj_from);
		getProjParams (sqlite, 4326, &proj_to);
		if (proj_to == NULL || proj_from == NULL)
		  {
		      if (proj_from)
			  free (proj_from);
		      if (proj_to)
			  free (proj_to);
		      sqlite3_result_null (context);
		      goto stop;
		  }
		if (data != NULL)
		    geo_wgs84 = gaiaTransform_r (data, geo, proj_from, proj_to);
		else
		    geo_wgs84 = gaiaTransform (geo, proj_from, proj_to);
		free (proj_from);
		free (proj_to);
		if (!geo_wgs84)
		  {
		      sqlite3_result_null (context);
		      goto stop;
		  }
		/* ok, reprojection was successful */
		gaiaFreeGeomColl (geo);
		geo = geo_wgs84;
	    }
	  /* produce KML-notation - actual work is done in gaiageo/gg_wkt.c */
	  gaiaOutBareKml (&out_buf, geo, precision);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
  stop:
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsKml3 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsKml(Anything name, Anything description, BLOB encoded geometry [, Integer precision])
/
/ returns the corresponding 'full' KML representation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geo_wgs84;
    sqlite3_int64 int_value;
    double dbl_value;
    const char *name;
    const char *desc;
    char *name_malloc = NULL;
    char *desc_malloc = NULL;
    char dummy[128];
    char *xdummy;
    char *proj_from;
    char *proj_to;
    int precision = 15;
    void *data = sqlite3_user_data (context);
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    switch (sqlite3_value_type (argv[0]))
      {
      case SQLITE_TEXT:
	  name = (const char *) sqlite3_value_text (argv[0]);
	  len = strlen (name);
	  name_malloc = malloc (len + 1);
	  strcpy (name_malloc, name);
	  name = name_malloc;
	  break;
      case SQLITE_INTEGER:
	  int_value = sqlite3_value_int64 (argv[0]);
	  sprintf (dummy, FRMT64, int_value);
	  len = strlen (dummy);
	  name_malloc = malloc (len + 1);
	  strcpy (name_malloc, dummy);
	  name = name_malloc;
	  break;
      case SQLITE_FLOAT:
	  dbl_value = sqlite3_value_double (argv[0]);
	  xdummy = sqlite3_mprintf ("%1.6f", dbl_value);
	  len = strlen (xdummy);
	  name_malloc = malloc (len + 1);
	  strcpy (name_malloc, xdummy);
	  sqlite3_free (xdummy);
	  name = name_malloc;
	  break;
      case SQLITE_BLOB:
	  name = "BLOB";
	  break;
      default:
	  name = "NULL";
	  break;
      };
    switch (sqlite3_value_type (argv[1]))
      {
      case SQLITE_TEXT:
	  desc = (const char *) sqlite3_value_text (argv[1]);
	  len = strlen (desc);
	  desc_malloc = malloc (len + 1);
	  strcpy (desc_malloc, desc);
	  desc = desc_malloc;
	  break;
      case SQLITE_INTEGER:
	  int_value = sqlite3_value_int64 (argv[1]);
	  sprintf (dummy, FRMT64, int_value);
	  len = strlen (dummy);
	  desc_malloc = malloc (len + 1);
	  strcpy (desc_malloc, dummy);
	  desc = desc_malloc;
	  break;
      case SQLITE_FLOAT:
	  dbl_value = sqlite3_value_double (argv[1]);
	  xdummy = sqlite3_mprintf ("%1.6f", dbl_value);
	  len = strlen (xdummy);
	  desc_malloc = malloc (len + 1);
	  strcpy (desc_malloc, xdummy);
	  sqlite3_free (xdummy);
	  desc = desc_malloc;
	  break;
      case SQLITE_BLOB:
	  desc = "BLOB";
	  break;
      default:
	  desc = "NULL";
	  break;
      };
    gaiaOutBufferInitialize (&out_buf);
    if (sqlite3_value_type (argv[2]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
    n_bytes = sqlite3_value_bytes (argv[2]);
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[3]);
	  else
	    {
		sqlite3_result_null (context);
		goto stop;
	    }
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (geo->Srid == 4326)
	      ;			/* already WGS84 */
	  else if (geo->Srid == 0)
	    {
		/* unknown SRID: giving up */
		sqlite3_result_null (context);
		goto stop;
	    }
	  else
	    {
		/* attempting to reproject into WGS84 */
		getProjParams (sqlite, geo->Srid, &proj_from);
		getProjParams (sqlite, 4326, &proj_to);
		if (proj_to == NULL || proj_from == NULL)
		  {
		      if (proj_from != NULL)
			  free (proj_from);
		      if (proj_to != NULL)
			  free (proj_to);
		      sqlite3_result_null (context);
		      goto stop;
		  }
		if (data != NULL)
		    geo_wgs84 = gaiaTransform_r (data, geo, proj_from, proj_to);
		else
		    geo_wgs84 = gaiaTransform (geo, proj_from, proj_to);
		free (proj_from);
		free (proj_to);
		if (!geo_wgs84)
		  {
		      sqlite3_result_null (context);
		      goto stop;
		  }
		/* ok, reprojection was successful */
		gaiaFreeGeomColl (geo);
		geo = geo_wgs84;
	    }
	  /* produce KML-notation - actual work is done in gaiageo/gg_wkt.c */
	  gaiaOutFullKml (&out_buf, name, desc, geo, precision);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
  stop:
    gaiaFreeGeomColl (geo);
    if (name_malloc)
	free (name_malloc);
    if (desc_malloc)
	free (desc_malloc);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsKml (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsKml(Anything name, Anything description, BLOB encoded geometry)
/     or
/ AsKml(BLOB encoded geometry)
/
/ returns the corresponding KML representation 
/ or NULL if any error is encountered
*/
    if (argc == 3 || argc == 4)
	fnct_AsKml3 (context, argc, argv);
    else
	fnct_AsKml1 (context, argc, argv);
}
#endif /* end including PROJ.4 */

static void
fnct_AsGml (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsGml(BLOB encoded geometry)
/    or
/ AsGml(integer version, BLOB encoded geometry)
/    or
/ AsGml(integer version, BLOB encoded geometry, integer precision)
/
/ *version* may be 2 (GML 2.1.2) or 3 (GML 3.1.1)
/ default *version*: 2
/
/ *precision* is the number of output decimal digits
/ default *precision*: 15
/
/ returns the corresponding GML representation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int version = 2;
    int precision = 15;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	      version = sqlite3_value_int (argv[0]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
	  n_bytes = sqlite3_value_bytes (argv[1]);
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 2)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER
	      && sqlite3_value_type (argv[1]) == SQLITE_BLOB)
	    {
		version = sqlite3_value_int (argv[0]);
		p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
		n_bytes = sqlite3_value_bytes (argv[1]);
	    }
	  else if (sqlite3_value_type (argv[0]) == SQLITE_BLOB
		   && sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
		n_bytes = sqlite3_value_bytes (argv[0]);
		precision = sqlite3_value_int (argv[1]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
      }
    gaiaOutBufferInitialize (&out_buf);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  /* produce GML-notation - actual work is done in gaiageo/gg_wkt.c */
	  gaiaOutGml (&out_buf, version, precision, geo);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsGeoJSON (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsGeoJSON(BLOB encoded geometry)
/    or
/ AsGeoJSON(BLOB encoded geometry, integer precision)
/    or
/ AsGeoJSON(BLOB encoded geometry, integer precision, integer options)
/
/ *precision* is the number of output decimal digits
/ default *precision*: 15
/
/ *options* may be one of the followings:
/   0 = no options [default]
/   1 = GeoJSON MBR
/   2 = GeoJSON Short CRS (e.g EPSG:4326) 
/   3 = 1 + 2 (Mbr + shortCrs)
/   4 = GeoJSON Long CRS (e.g urn:ogc:def:crs:EPSG::4326)
/   5 = 1 + 4 (Mbr + longCrs)
/
/ returns the corresponding GML representation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int precision = 15;
    int options = 0;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	gpkg_amphibious = cache->gpkg_mode = cache->gpkg_mode;;
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB
	      && sqlite3_value_type (argv[1]) == SQLITE_INTEGER
	      && sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
		n_bytes = sqlite3_value_bytes (argv[0]);
		precision = sqlite3_value_int (argv[1]);
		options = sqlite3_value_int (argv[2]);
		if (options >= 1 && options <= 5)
		    ;
		else
		    options = 0;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 2)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB
	      && sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
		n_bytes = sqlite3_value_bytes (argv[0]);
		precision = sqlite3_value_int (argv[1]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
      }
    gaiaOutBufferInitialize (&out_buf);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  /* produce GeoJSON-notation - actual work is done in gaiageo/gg_wkt.c */
	  gaiaOutGeoJSON (&out_buf, geo, precision, options);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_AsBinary (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsBinary(BLOB encoded geometry)
/
/ returns the corresponding WKB encoded value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaToWkb (geo, &p_result, &len);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_AsFGF (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsFGF(BLOB encoded geometry, int dims)
/
/ returns the corresponding FGF encoded value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int coord_dims;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  spatialite_e
	      ("AsFGF() error: argument 2 [geom_coords] is not of the Integer type\n");
	  sqlite3_result_null (context);
	  return;
      }
    coord_dims = sqlite3_value_int (argv[1]);
    if (coord_dims
	== 0 || coord_dims == 1 || coord_dims == 2 || coord_dims == 3)
	;
    else
      {
	  spatialite_e
	      ("AsFGF() error: argument 2 [geom_coords] out of range [0,1,2,3]\n");
	  sqlite3_result_null (context);
	  return;
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaToFgf (geo, &p_result, &len, coord_dims);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_tiny_point_encode (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ TinyPointEncode(variable-type)
/
/ returns a BLOB TinyPoint if the received argument is a BLOB-GEOMETRY POINT
/ in any other case the received argument will be returned "as is"
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  int geom_point = 1;
	  const unsigned char *blob =
	      (const unsigned char *) sqlite3_value_blob (argv[0]);
	  int size = sqlite3_value_bytes (argv[0]);
	  if (size < 45)
	      geom_point = 0;
	  else
	    {
		int endian_arch = gaiaEndianArch ();
		int type;
		int little_endian = 0;
		if (*(blob + 0) != GAIA_MARK_START)
		    geom_point = 0;
		if (*(blob + (size - 1)) != GAIA_MARK_END)
		    geom_point = 0;
		if (*(blob + 38) != GAIA_MARK_MBR)
		    geom_point = 0;
		if (*(blob + 1) == GAIA_LITTLE_ENDIAN)
		    little_endian = 1;
		else if (*(blob + 1) == GAIA_BIG_ENDIAN)
		    ;
		else
		    geom_point = 0;
		type = gaiaImport32 (blob + 39, little_endian, endian_arch);
		if (type == GAIA_POINT || type == GAIA_POINTZ
		    || type == GAIA_POINTM || type == GAIA_POINTZM)
		    ;
		else
		    geom_point = 0;
	    }
	  if (geom_point)
	    {
		int endian_arch = gaiaEndianArch ();
		int type;
		int little_endian = 0;
		int srid;
		double x;
		double y;
		double z;
		double m;
		unsigned char *out;
		int out_sz;
		if (*(blob + 1) == GAIA_LITTLE_ENDIAN)
		    little_endian = 1;
		srid = gaiaImport32 (blob + 2, little_endian, endian_arch);
		type = gaiaImport32 (blob + 39, little_endian, endian_arch);
		x = gaiaImport64 (blob + 43, little_endian, endian_arch);
		y = gaiaImport64 (blob + 51, little_endian, endian_arch);
		switch (type)
		  {
		  case GAIA_POINT:
		      gaiaMakePointEx (1, x, y, srid, &out, &out_sz);
		      break;
		  case GAIA_POINTZ:
		      z = gaiaImport64 (blob + 59, little_endian, endian_arch);
		      gaiaMakePointZEx (1, x, y, z, srid, &out, &out_sz);
		      break;
		  case GAIA_POINTM:
		      m = gaiaImport64 (blob + 59, little_endian, endian_arch);
		      gaiaMakePointMEx (1, x, y, m, srid, &out, &out_sz);
		      break;
		  case GAIA_POINTZM:
		      z = gaiaImport64 (blob + 59, little_endian, endian_arch);
		      m = gaiaImport64 (blob + 67, little_endian, endian_arch);
		      gaiaMakePointZMEx (1, x, y, z, m, srid, &out, &out_sz);
		      break;
		  };
		sqlite3_result_blob (context, out, out_sz, free);
	    }
	  else
	      sqlite3_result_blob (context, blob, size, SQLITE_TRANSIENT);

      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	sqlite3_result_int (context, sqlite3_value_int (argv[0]));
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	sqlite3_result_double (context, sqlite3_value_double (argv[0]));
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	sqlite3_result_text (context,
			     (const char *) sqlite3_value_text (argv[0]),
			     sqlite3_value_bytes (argv[0]), SQLITE_TRANSIENT);
    else
	sqlite3_result_null (context);
}

static void
fnct_geometry_point_encode (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ GeometryPointEncode(variable-type)
/
/ returns a BLOB GEOMETRY if the received argument is a BLOB-TinyPoint POINT
/ in any other case the received argument will be returned "as is"
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  int tiny_point = 1;
	  const unsigned char *blob =
	      (const unsigned char *) sqlite3_value_blob (argv[0]);
	  int size = sqlite3_value_bytes (argv[0]);
	  if (size < 24)
	      tiny_point = 0;
	  else
	    {
		if (size == 24 || size == 32 || size == 40)
		    ;
		else
		    tiny_point = 0;
		if (*(blob + 0) != GAIA_MARK_START)
		    tiny_point = 0;
		if (*(blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN
		    || *(blob + 1) == GAIA_TINYPOINT_BIG_ENDIAN)
		    ;
		else
		    tiny_point = 0;
		if (*(blob + 6) == GAIA_TINYPOINT_XY
		    || *(blob + 6) == GAIA_TINYPOINT_XYZ
		    || *(blob + 6) == GAIA_TINYPOINT_XYM
		    || *(blob + 6) == GAIA_TINYPOINT_XYZM)
		    ;
		else
		    tiny_point = 0;
		if (*(blob + (size - 1)) != GAIA_MARK_END)
		    tiny_point = 0;
	    }
	  if (tiny_point)
	    {
		int endian_arch = gaiaEndianArch ();
		int type = *(blob + 6);
		int little_endian = 0;
		int srid;
		double x;
		double y;
		double z;
		double m;
		unsigned char *out;
		int out_sz;
		if (*(blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN)
		    little_endian = 1;
		srid = gaiaImport32 (blob + 2, little_endian, endian_arch);
		x = gaiaImport64 (blob + 7, little_endian, endian_arch);
		y = gaiaImport64 (blob + 15, little_endian, endian_arch);
		switch (type)
		  {
		  case GAIA_TINYPOINT_XY:
		      gaiaMakePointEx (0, x, y, srid, &out, &out_sz);
		      break;
		  case GAIA_TINYPOINT_XYZ:
		      z = gaiaImport64 (blob + 23, little_endian, endian_arch);
		      gaiaMakePointZEx (0, x, y, z, srid, &out, &out_sz);
		      break;
		  case GAIA_TINYPOINT_XYM:
		      m = gaiaImport64 (blob + 23, little_endian, endian_arch);
		      gaiaMakePointMEx (0, x, y, m, srid, &out, &out_sz);
		      break;
		  case GAIA_TINYPOINT_XYZM:
		      z = gaiaImport64 (blob + 23, little_endian, endian_arch);
		      m = gaiaImport64 (blob + 31, little_endian, endian_arch);
		      gaiaMakePointZMEx (0, x, y, z, m, srid, &out, &out_sz);
		      break;
		  };
		sqlite3_result_blob (context, out, out_sz, free);
	    }
	  else
	      sqlite3_result_blob (context, blob, size, SQLITE_TRANSIENT);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	sqlite3_result_int (context, sqlite3_value_int (argv[0]));
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	sqlite3_result_double (context, sqlite3_value_double (argv[0]));
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	sqlite3_result_text (context,
			     (const char *) sqlite3_value_text (argv[0]),
			     sqlite3_value_bytes (argv[0]), SQLITE_TRANSIENT);
    else
	sqlite3_result_null (context);
}

static void
fnct_MakePoint1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePoint(double X, double Y)
/    alias
/ ST_Point(double X, double Y)
/
/ builds a POINT 
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointEx (tiny_point, x, y, 0, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePoint2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePoint(double X, double Y, int SRID)
/
/ builds a POINT 
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    int srid;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointEx (tiny_point, x, y, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointZ1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointZ(double X, double Y, double Z)
/
/ builds a POINT Z 
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double z;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	z = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  z = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointZEx (tiny_point, x, y, z, 0, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointZ2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointZ(double X, double Y, double Z, int SRID)
/
/ builds a POINT Z
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double z;
    int srid;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	z = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  z = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointZEx (tiny_point, x, y, z, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointM1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointM(double X, double Y, double M)
/
/ builds a POINT M
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double m;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	m = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  m = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointMEx (tiny_point, x, y, m, 0, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointM2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointM(double X, double Y, double M, int SRID)
/
/ builds a POINT M
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double m;
    int srid;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	m = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  m = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointMEx (tiny_point, x, y, m, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointZM1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointZM(double X, double Y, double Z, double M)
/
/ builds a POINT ZM 
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double z;
    double m;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	z = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  z = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	m = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  m = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointZMEx (tiny_point, x, y, z, m, 0, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_MakePointZM2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakePointZM(double X, double Y, double Z, double M, int SRID)
/
/ builds a POINT 
/ or NULL if any error is encountered
*/
    int len;
    int int_value;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double z;
    double m;
    int srid;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	tiny_point = cache->tinyPointEnabled;
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	z = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  z = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	m = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  m = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaMakePointZMEx (tiny_point, x, y, z, m, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
addGeomPointToDynamicLine (gaiaDynamicLinePtr dyn, gaiaGeomCollPtr geom)
{
/* appending a simple-Point Geometry to a Dynamic Line */
    int pts;
    int lns;
    int pgs;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;

    if (dyn == NULL)
	return;
    if (dyn->Error)
	return;
/* checking if GEOM simply is a POINT */
    if (geom == NULL)
      {
	  dyn->Error = 1;
	  return;
      }
    pts = 0;
    lns = 0;
    pgs = 0;
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts == 1 && lns == 0 && pgs == 0)
	;
    else
      {
	  /* failure: not a simple POINT */
	  dyn->Error = 1;
	  return;
      }

    if (dyn->Srid != geom->Srid)
      {
	  /* failure: SRID mismatch */
	  dyn->Error = 1;
	  return;
      }

    switch (geom->FirstPoint->DimensionModel)
      {
      case GAIA_XY_Z_M:
	  gaiaAppendPointZMToDynamicLine (dyn, geom->FirstPoint->X,
					  geom->FirstPoint->Y,
					  geom->FirstPoint->Z,
					  geom->FirstPoint->M);
	  break;
      case GAIA_XY_Z:
	  gaiaAppendPointZToDynamicLine (dyn, geom->FirstPoint->X,
					 geom->FirstPoint->Y,
					 geom->FirstPoint->Z);
	  break;
      case GAIA_XY_M:
	  gaiaAppendPointMToDynamicLine (dyn, geom->FirstPoint->X,
					 geom->FirstPoint->Y,
					 geom->FirstPoint->M);
	  break;
      default:
	  gaiaAppendPointToDynamicLine (dyn, geom->FirstPoint->X,
					geom->FirstPoint->Y);
	  break;
      }
}

static void
fnct_MakeLine_step (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeLine(BLOBencoded geom)
/
/ aggregate function - STEP
/
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    gaiaDynamicLinePtr *p;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geom)
	return;
    p = sqlite3_aggregate_context (context, sizeof (gaiaDynamicLinePtr));
    if (!(*p))
      {
	  /* this is the first row */
	  *p = gaiaAllocDynamicLine ();
	  (*p)->Srid = geom->Srid;
	  addGeomPointToDynamicLine (*p, geom);
	  gaiaFreeGeomColl (geom);
      }
    else
      {
	  /* subsequent rows */
	  addGeomPointToDynamicLine (*p, geom);
	  gaiaFreeGeomColl (geom);
      }
}

static gaiaGeomCollPtr
geomFromDynamicLine (gaiaDynamicLinePtr dyn)
{
/* attempting to build a Geometry from a Dynamic Line */
    gaiaGeomCollPtr geom = NULL;
    gaiaLinestringPtr ln = NULL;
    gaiaPointPtr pt;
    int iv;
    int count = 0;
    int dims = GAIA_XY;

    if (dyn == NULL)
	return NULL;
    if (dyn->Error)
	return NULL;

    pt = dyn->First;
    while (pt)
      {
	  /* counting points and checking dims */
	  count++;
	  if (dims == GAIA_XY && pt->DimensionModel != GAIA_XY)
	      dims = pt->DimensionModel;
	  if (dims == GAIA_XY_Z
	      && (pt->DimensionModel == GAIA_XY_M
		  || pt->DimensionModel == GAIA_XY_Z_M))
	      dims = GAIA_XY_Z_M;
	  if (dims == GAIA_XY_M
	      && (pt->DimensionModel == GAIA_XY_Z
		  || pt->DimensionModel == GAIA_XY_Z_M))
	      dims = GAIA_XY_Z_M;
	  pt = pt->Next;
      }
    if (count < 2)
	return NULL;

    switch (dims)
      {
      case GAIA_XY_Z_M:
	  geom = gaiaAllocGeomCollXYZM ();
	  ln = gaiaAllocLinestringXYZM (count);
	  break;
      case GAIA_XY_Z:
	  geom = gaiaAllocGeomCollXYZ ();
	  ln = gaiaAllocLinestringXYZ (count);
	  break;
      case GAIA_XY_M:
	  geom = gaiaAllocGeomCollXYM ();
	  ln = gaiaAllocLinestringXYM (count);
	  break;
      default:
	  geom = gaiaAllocGeomColl ();
	  ln = gaiaAllocLinestring (count);
	  break;
      };

    if (geom != NULL && ln != NULL)
      {
	  gaiaInsertLinestringInGeomColl (geom, ln);
	  geom->Srid = dyn->Srid;
      }
    else
      {
	  if (geom)
	      gaiaFreeGeomColl (geom);
	  if (ln)
	      gaiaFreeLinestring (ln);
	  return NULL;
      }

    iv = 0;
    pt = dyn->First;
    while (pt)
      {
	  /* setting linestring points */
	  if (dims == GAIA_XY_Z_M)
	    {
		gaiaSetPointXYZM (ln->Coords, iv, pt->X, pt->Y, pt->Z, pt->M);
	    }
	  else if (dims == GAIA_XY_Z)
	    {
		gaiaSetPointXYZ (ln->Coords, iv, pt->X, pt->Y, pt->Z);
	    }
	  else if (dims == GAIA_XY_M)
	    {
		gaiaSetPointXYM (ln->Coords, iv, pt->X, pt->Y, pt->M);
	    }
	  else
	    {
		gaiaSetPoint (ln->Coords, iv, pt->X, pt->Y);
	    }
	  iv++;
	  pt = pt->Next;
      }
    return geom;
}

static void
fnct_MakeLine_final (sqlite3_context * context)
{
/* SQL function:
/ MakeLine(BLOBencoded geom)
/
/ aggregate function - FINAL
/
*/
    gaiaGeomCollPtr result;
    gaiaDynamicLinePtr *p = sqlite3_aggregate_context (context, 0);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    result = geomFromDynamicLine (*p);
    gaiaFreeDynamicLine (*p);
    if (!result)
	sqlite3_result_null (context);
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
}

static void
buildLineFromMultiPoint (sqlite3_context * context, gaiaGeomCollPtr geom,
			 int direction)
{
/* internal: building a Linestring from a MultiPolygon */
    gaiaGeomCollPtr result;
    gaiaDynamicLinePtr dyn;
    int n_pts = 0;
    int n_lns = 0;
    int n_pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (geom)
      {
	  pt = geom->FirstPoint;
	  while (pt)
	    {
		n_pts++;
		pt = pt->Next;
	    }
	  ln = geom->FirstLinestring;
	  while (ln)
	    {
		n_lns++;
		ln = ln->Next;
	    }
	  pg = geom->FirstPolygon;
	  while (pg)
	    {
		n_pgs++;
		pg = pg->Next;
	    }
      }
    /* checking if really is a MultiPoint */
    if (n_pts >= 2 && n_lns == 0 && n_pgs == 0)
	;
    else
      {
	  sqlite3_result_null (context);
	  goto end;
      }
    dyn = gaiaAllocDynamicLine ();
    dyn->Srid = geom->Srid;
    pt = geom->FirstPoint;
    while (pt)
      {
	  /* inserting all Points accordingly to required direction */
	  if (direction)
	    {
		/* conformant direction */
		switch (pt->DimensionModel)
		  {
		  case GAIA_XY_Z_M:
		      gaiaAppendPointZMToDynamicLine (dyn, pt->X, pt->Y,
						      pt->Z, pt->M);
		      break;
		  case GAIA_XY_Z:
		      gaiaAppendPointZToDynamicLine (dyn, pt->X, pt->Y, pt->Z);
		      break;
		  case GAIA_XY_M:
		      gaiaAppendPointMToDynamicLine (dyn, pt->X, pt->Y, pt->M);
		      break;
		  default:
		      gaiaAppendPointToDynamicLine (dyn, pt->X, pt->Y);
		      break;
		  }
	    }
	  else
	    {
		/* reverse direction */
		switch (pt->DimensionModel)
		  {
		  case GAIA_XY_Z_M:
		      gaiaPrependPointZMToDynamicLine (dyn, pt->X, pt->Y,
						       pt->Z, pt->M);
		      break;
		  case GAIA_XY_Z:
		      gaiaPrependPointZToDynamicLine (dyn, pt->X, pt->Y, pt->Z);
		      break;
		  case GAIA_XY_M:
		      gaiaPrependPointMToDynamicLine (dyn, pt->X, pt->Y, pt->M);
		      break;
		  default:
		      gaiaPrependPointToDynamicLine (dyn, pt->X, pt->Y);
		      break;
		  }
	    }
	  pt = pt->Next;
      }
    result = geomFromDynamicLine (dyn);
    gaiaFreeDynamicLine (dyn);
    if (!result)
	sqlite3_result_null (context);
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
  end:
    gaiaFreeGeomColl (geom);
}

static void
fnct_MakeLine (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeLine(point-geometry geom1, point-geometry geom2)
/     or
/ MakeLine(multipoint geom, boolean direction)
/
/ - builds a SEGMENT joining two POINTs 
/ - the MultiPoint version works exactely as the corresponding aggregate
/   function, but not requiring aggregation; direction=TRUE direct order,
/   direction=FALSE reverse order
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_blob;
    int n_bytes;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  /* expecting a single MultiPoint input */
	  int direction = sqlite3_value_int (argv[1]);
	  buildLineFromMultiPoint (context, geo1, direction);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo2)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    gaiaMakeLine (geo1, geo2, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
  stop:
    if (geo1)
	gaiaFreeGeomColl (geo1);
    if (geo2)
	gaiaFreeGeomColl (geo2);
}

static void
fnct_MakeCircle (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeCircle(double cx, double cy, double radius)
/     or
/ MakeCircle(double cx, double cy, double radius, int srid)
/     or
/ MakeCircle(double cx, double cy, double radius, int srid, double step)
/
/ - builds a Linestring approximating a Circle
/ - step is the angular distance (in degrees) between points on 
/   the circurmference (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    int ival;
    double cx;
    double cy;
    double r;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  r = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	r = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[3]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 5)
      {
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[4]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[4]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    geom = gaiaMakeCircle (cx, cy, r, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  if (srid != 0)
	      geom->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
}

static void
fnct_MakeArc (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeArc(double cx, double cy, double radius, double start, double stop)
/     or
/ MakeArc(double cx, double cy, double radius, double start, double stop, 
/         int srid)
/     or
/ MakeArc(double cx, double cy, double radius, double start, double stop, 
/         int srid, double step)
/
/ - builds a Linestring approximating a Circular Arc
/ - start and stop are the initial and final angles (in degrees)
/ - step is the angular distance (in degrees) between points on 
/   the circurmference (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    int ival;
    double cx;
    double cy;
    double r;
    double start;
    double stop;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  r = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	r = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  start = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	start = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[4]);
	  stop = ival;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	stop = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[5]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 7)
      {
	  if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[6]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[6]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    geom = gaiaMakeArc (cx, cy, r, start, stop, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  if (srid != 0)
	      geom->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
}

static void
fnct_MakeEllipse (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeEllipse(double cx, double cy, double x_axis, double y_axis)
/     or
/ MakeEllipse(double cx, double cy, double x_axis, double y_axis,
/            int srid)
/     or
/ MakeEllipse(double cx, double cy, double x_axis, double y_axis,
/             int srid, double step)
/
/ - builds a Linestring approximating an Ellipse
/ - step is the angular distance (in degrees) between points on 
/   the ellipse (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    int ival;
    double cx;
    double cy;
    double x_axis;
    double y_axis;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  x_axis = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x_axis = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  y_axis = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y_axis = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[4]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 6)
      {
	  if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[5]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[5]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    geom = gaiaMakeEllipse (cx, cy, x_axis, y_axis, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  if (srid != 0)
	      geom->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
}

static void
fnct_MakeEllipticArc (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ MakeEllipticArc(double cx, double cy, double x_axis, double y_axis, 
/                 double start, double stop)
/     or
/ MakeEllipticArc(double cx, double cy, double x_axis, double y_axis,
/                 double start, double stop, int srid)
/     or
/ MakeEllipticArc(double cx, double cy, double x_axis, double y_axis,
/                 double start, double stop, int srid, double step)
/
/ - builds a Linestring approximating an Elliptic Arc
/ - start and stop are the initial and final angles (in degrees)
/ - step is the angular distance (in degrees) between points on 
/   the ellipse (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    int ival;
    double cx;
    double cy;
    double x_axis;
    double y_axis;
    double start;
    double stop;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  x_axis = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x_axis = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  y_axis = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y_axis = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[4]);
	  start = ival;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	start = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[5]);
	  stop = ival;
      }
    else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	stop = sqlite3_value_double (argv[5]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 7)
      {
	  if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[6]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 8)
      {
	  if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[7]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[7]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    geom = gaiaMakeEllipticArc (cx, cy, x_axis, y_axis, start, stop, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  if (srid != 0)
	      geom->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
}

static void
fnct_MakeCircularSector (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ MakeCircularSector(double cx, double cy, double radius, double start, double stop)
/     or
/ MakeCircularSector(double cx, double cy, double radius, double start, double stop, 
/         int srid)
/     or
/ MakeCircularSector(double cx, double cy, double radius, double start, double stop, 
/         int srid, double step)
/
/ - builds a Polygon approximating a Circular Sector
/ - start and stop are the initial and final angles (in degrees)
/ - step is the angular distance (in degrees) between points on 
/   the circurmference (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    gaiaGeomCollPtr sector = NULL;
    int ival;
    double cx;
    double cy;
    double r;
    double start;
    double stop;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  r = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	r = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  start = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	start = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[4]);
	  stop = ival;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	stop = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[5]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 7)
      {
	  if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[6]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[6]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    geom = gaiaMakeArc (cx, cy, r, start, stop, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  int ii;
	  int io = 0;
	  double x;
	  double y;
	  gaiaLinestringPtr in = geom->FirstLinestring;
	  gaiaPolygonPtr pg;
	  gaiaRingPtr out;
	  sector = gaiaAllocGeomColl ();
	  pg = gaiaAddPolygonToGeomColl (sector, in->Points + 2, 0);
	  out = pg->Exterior;
	  /* inserting the Centre - first point */
	  gaiaSetPoint (out->Coords, io, cx, cy);
	  io++;
	  for (ii = 0; ii < in->Points; ii++)
	    {
		/* copying the Arc's points */
		gaiaGetPoint (in->Coords, ii, &x, &y);
		gaiaSetPoint (out->Coords, io, x, y);
		io++;
	    }
	  /* inserting the Centre - last point */
	  gaiaSetPoint (out->Coords, io, cx, cy);
	  if (srid != 0)
	      sector->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (sector, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
    if (sector)
	gaiaFreeGeomColl (sector);
}

static void
fnct_MakeCircularStripe (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ MakeCircularStripe(double cx, double cy, double radius_1, double radius_2,
/                    double start, double stop)
/     or
/ MakeCircularStripe(double cx, double cy, double radius_1, double radius_2,
/                    double start, double stop, int srid)
/     or
/ MakeCircularStripe(double cx, double cy, double radius_1, double radius_2, 
/                    double start, double stop, int srid, double step)
/
/ - builds a Polygon approximating a Circular Stripe delimited by two
/   arcs sharing the same Centre-Point but having different radii
/ - start and stop are the initial and final angles (in degrees)
/ - step is the angular distance (in degrees) between points on 
/   the circurmference (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr arc1 = NULL;
    gaiaGeomCollPtr arc2 = NULL;
    gaiaGeomCollPtr stripe = NULL;
    int ival;
    double cx;
    double cy;
    double r1;
    double r2;
    double start;
    double stop;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  r1 = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	r1 = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  r2 = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	r2 = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[4]);
	  start = ival;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	start = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[5]);
	  stop = ival;
      }
    else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	stop = sqlite3_value_double (argv[5]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 7)
      {
	  if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[6]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 8)
      {
	  if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[7]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[7]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    arc1 = gaiaMakeArc (cx, cy, r1, start, stop, step);
    arc2 = gaiaMakeArc (cx, cy, r2, start, stop, step);
    if (arc1 == NULL || arc2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  int ii;
	  int io = 0;
	  double x;
	  double y;
	  gaiaLinestringPtr in1 = arc1->FirstLinestring;
	  gaiaLinestringPtr in2 = arc2->FirstLinestring;
	  gaiaPolygonPtr pg;
	  gaiaRingPtr out;
	  stripe = gaiaAllocGeomColl ();
	  pg = gaiaAddPolygonToGeomColl (stripe,
					 in1->Points + in2->Points + 1, 0);
	  out = pg->Exterior;
	  for (ii = 0; ii < in1->Points; ii++)
	    {
		/* copying the first Arc's points - direct order */
		gaiaGetPoint (in1->Coords, ii, &x, &y);
		gaiaSetPoint (out->Coords, io, x, y);
		io++;
	    }
	  for (ii = in2->Points - 1; ii >= 0; ii--)
	    {
		/* copying the second Arc's points - reverse order */
		gaiaGetPoint (in2->Coords, ii, &x, &y);
		gaiaSetPoint (out->Coords, io, x, y);
		io++;
	    }
	  /* closing the Polygon Ring */
	  gaiaGetPoint (out->Coords, 0, &x, &y);
	  gaiaSetPoint (out->Coords, io, x, y);
	  if (srid != 0)
	      stripe->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (stripe, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (arc1)
	gaiaFreeGeomColl (arc1);
    if (arc2)
	gaiaFreeGeomColl (arc2);
    if (stripe)
	gaiaFreeGeomColl (stripe);
}

static void
fnct_MakeEllipticSector (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ MakeEllipticSector(double cx, double cy, double x_axis, double y_axis, 
/                 double start, double stop)
/     or
/ MakeEllipticSector(double cx, double cy, double x_axis, double y_axis,
/                 double start, double stop, int srid)
/     or
/ MakeEllipticSector(double cx, double cy, double x_axis, double y_axis,
/                 double start, double stop, int srid, double step)
/
/ - builds a Polygon approximating an Elliptic Sector
/ - start and stop are the initial and final angles (in degrees)
/ - step is the angular distance (in degrees) between points on 
/   the ellipse (by default: every 10 degs) 
/ - or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geom = NULL;
    gaiaGeomCollPtr sector = NULL;
    int ival;
    double cx;
    double cy;
    double x_axis;
    double y_axis;
    double start;
    double stop;
    int srid = 0;
    double step = 10.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[0]);
	  cx = ival;
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	cx = sqlite3_value_double (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  cy = ival;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	cy = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  x_axis = ival;
      }
    else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x_axis = sqlite3_value_double (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[3]);
	  y_axis = ival;
      }
    else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y_axis = sqlite3_value_double (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[4]);
	  start = ival;
      }
    else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	start = sqlite3_value_double (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[5]);
	  stop = ival;
      }
    else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	stop = sqlite3_value_double (argv[5]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 7)
      {
	  if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[6]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 8)
      {
	  if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
	    {
		ival = sqlite3_value_int (argv[7]);
		step = ival;
	    }
	  else if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	      step = sqlite3_value_double (argv[7]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    geom = gaiaMakeEllipticArc (cx, cy, x_axis, y_axis, start, stop, step);
    if (!geom)
	sqlite3_result_null (context);
    else
      {
	  int ii;
	  int io = 0;
	  double x;
	  double y;
	  gaiaLinestringPtr in = geom->FirstLinestring;
	  gaiaPolygonPtr pg;
	  gaiaRingPtr out;
	  sector = gaiaAllocGeomColl ();
	  pg = gaiaAddPolygonToGeomColl (sector, in->Points + 2, 0);
	  out = pg->Exterior;
	  /* inserting the Centre - first point */
	  gaiaSetPoint (out->Coords, io, cx, cy);
	  io++;
	  for (ii = 0; ii < in->Points; ii++)
	    {
		/* copying the Arc's points */
		gaiaGetPoint (in->Coords, ii, &x, &y);
		gaiaSetPoint (out->Coords, io, x, y);
		io++;
	    }
	  /* inserting the Centre - last point */
	  gaiaSetPoint (out->Coords, io, cx, cy);
	  if (srid != 0)
	      sector->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (sector, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom)
	gaiaFreeGeomColl (geom);
    if (sector)
	gaiaFreeGeomColl (sector);
}

static void
fnct_Collect_step (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Collect(BLOBencoded geom)
/
/ aggregate function - STEP
/
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    gaiaGeomCollPtr result;
    gaiaGeomCollPtr *p;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geom)
	return;
    p = sqlite3_aggregate_context (context, sizeof (gaiaGeomCollPtr));
    if (!(*p))
      {
	  /* this is the first row */
	  *p = geom;
      }
    else
      {
	  /* subsequent rows */
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaMergeGeometries_r (data, *p, geom);
	  else
	      result = gaiaMergeGeometries (*p, geom);
	  *p = result;
	  gaiaFreeGeomColl (geom);
      }
}

static void
fnct_Collect_final (sqlite3_context * context)
{
/* SQL function:
/ Collect(BLOBencoded geom)
/
/ aggregate function - FINAL
/
*/
    gaiaGeomCollPtr result;
    gaiaGeomCollPtr *p = sqlite3_aggregate_context (context, 0);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    result = *p;
    if (!result)
	sqlite3_result_null (context);
    else if (gaiaIsEmpty (result))
      {
	  gaiaFreeGeomColl (result);
	  sqlite3_result_null (context);
      }
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
}

static void
fnct_Collect (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Collect(geometry geom1, geometry geom2)
/
/ merges two generic GEOMETRIES into a single one 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
      {
	  if (geo1 != NULL)
	      gaiaFreeGeomColl (geo1);
	  if (geo2 != NULL)
	      gaiaFreeGeomColl (geo2);
	  geo1 = NULL;
	  geo2 = NULL;
	  sqlite3_result_null (context);
      }
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaMergeGeometries_r (data, geo1, geo2);
	  else
	      result = gaiaMergeGeometries (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else if (gaiaIsEmpty (result))
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo2);
}

static void
geom_from_text1 (sqlite3_context * context, int argc, sqlite3_value ** argv,
		 short type)
{
/* SQL function:
/ GeomFromText(WKT encoded geometry)
/
/ returns the current geometry by parsing WKT encoded string 
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, type);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
geom_from_text2 (sqlite3_context * context, int argc, sqlite3_value ** argv,
		 short type)
{
/* SQL function:
/ GeomFromText(WKT encoded geometry, SRID)
/
/ returns the current geometry by parsing WKT encoded string 
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, type);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static int
check_wkb (const unsigned char *wkb, int size, short type)
{
/* checking type coherency for WKB encoded GEOMETRY */
    int little_endian;
    int wkb_type;
    int endian_arch = gaiaEndianArch ();
    if (size < 5)
	return 0;		/* too short to be a WKB */
    if (*(wkb + 0) == 0x01)
	little_endian = GAIA_LITTLE_ENDIAN;
    else if (*(wkb + 0) == 0x00)
	little_endian = GAIA_BIG_ENDIAN;
    else
	return 0;		/* illegal byte ordering; neither BIG-ENDIAN nor LITTLE-ENDIAN */
    wkb_type = gaiaImport32 (wkb + 1, little_endian, endian_arch);
    if (wkb_type == GAIA_POINT || wkb_type == GAIA_LINESTRING
	|| wkb_type == GAIA_POLYGON || wkb_type == GAIA_MULTIPOINT
	|| wkb_type == GAIA_MULTILINESTRING || wkb_type == GAIA_MULTIPOLYGON
	|| wkb_type == GAIA_GEOMETRYCOLLECTION || wkb_type == GAIA_POINTZ
	|| wkb_type == GAIA_LINESTRINGZ || wkb_type == GAIA_POLYGONZ
	|| wkb_type == GAIA_MULTIPOINTZ || wkb_type == GAIA_MULTILINESTRINGZ
	|| wkb_type == GAIA_MULTIPOLYGONZ
	|| wkb_type == GAIA_GEOMETRYCOLLECTIONZ || wkb_type == GAIA_POINTM
	|| wkb_type == GAIA_LINESTRINGM || wkb_type == GAIA_POLYGONM
	|| wkb_type == GAIA_MULTIPOINTM || wkb_type == GAIA_MULTILINESTRINGM
	|| wkb_type == GAIA_MULTIPOLYGONM
	|| wkb_type == GAIA_GEOMETRYCOLLECTIONM || wkb_type == GAIA_POINTZM
	|| wkb_type == GAIA_LINESTRINGZM || wkb_type == GAIA_POLYGONZM
	|| wkb_type == GAIA_MULTIPOINTZM || wkb_type == GAIA_MULTILINESTRINGZM
	|| wkb_type == GAIA_MULTIPOLYGONZM
	|| wkb_type == GAIA_GEOMETRYCOLLECTIONZM)
	;
    else
	return 0;		/* illegal GEOMETRY CLASS */
    if (type < 0)
	;			/* no restrinction about GEOMETRY CLASS TYPE */
    else
      {
	  if (wkb_type != type)
	      return 0;		/* invalid CLASS TYPE for request */
      }
    return 1;
}

static void
geom_from_wkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv,
		short type)
{
/* SQL function:
/ GeomFromWKB(WKB encoded geometry)
/
/ returns the current geometry by parsing a WKB encoded blob 
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    int n_bytes;
    unsigned char *p_result = NULL;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, type))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
geom_from_wkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv,
		short type)
{
/* SQL function:
/ GeomFromWKB(WKB encoded geometry, SRID)
/
/ returns the current geometry by parsing a WKB encoded blob
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    int n_bytes;
    unsigned char *p_result = NULL;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, type))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_GeometryFromFGF1 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromFGF(FGF encoded geometry)
/
/ returns the current geometry by parsing an FGF encoded blob 
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    int n_bytes;
    unsigned char *p_result = NULL;
    const unsigned char *fgf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    fgf = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromFgf (fgf, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_GeometryFromFGF2 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromFGF(FGF encoded geometry, SRID)
/
/ returns the current geometry by parsing an FGF encoded string 
/ or NULL if any error is encountered
/
/ if *type* is a negative value can accept any GEOMETRY CLASS
/ otherwise only requests conforming with required CLASS are valid
*/
    int len;
    int n_bytes;
    unsigned char *p_result = NULL;
    const unsigned char *fgf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    fgf = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromFgf (fgf, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

/*
/ the following functions simply readdress the request to geom_from_text?()
/ setting the appropriate GEOMETRY CLASS TYPE
*/

static void
fnct_GeomFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) -1);
}

static void
fnct_GeomFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) -1);
}

static void
fnct_GeomCollFromText1 (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_GEOMETRYCOLLECTION);
}

static void
fnct_GeomCollFromText2 (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_GEOMETRYCOLLECTION);
}

static void
fnct_LineFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_LINESTRING);
}

static void
fnct_LineFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_LINESTRING);
}

static void
fnct_PointFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_POINT);
}

static void
fnct_PointFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_POINT);
}

static void
fnct_PolyFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_POLYGON);
}

static void
fnct_PolyFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_POLYGON);
}

static void
fnct_MLineFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_MULTILINESTRING);
}

static void
fnct_MLineFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_MULTILINESTRING);
}

static void
fnct_MPointFromText1 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_MULTIPOINT);
}

static void
fnct_MPointFromText2 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_MULTIPOINT);
}

static void
fnct_MPolyFromText1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text1 (context, argc, argv, (short) GAIA_MULTIPOLYGON);
}

static void
fnct_MPolyFromText2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_text2 (context, argc, argv, (short) GAIA_MULTIPOLYGON);
}

static void
fnct_WktToSql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_WKTToSQL(WKT encoded geometry)
/
/ returns the current geometry by parsing WKT encoded string 
/ or NULL if any error is encountered
/
/ the SRID is always 0 [SQL/MM function]
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, -1);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

/*
/ the following functions simply readdress the request to geom_from_wkb?()
/ setting the appropriate GEOMETRY CLASS TYPE
*/

static void
fnct_GeomFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) -1);
}

static void
fnct_GeomFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) -1);
}

static void
fnct_GeomCollFromWkb1 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_GEOMETRYCOLLECTION);
}

static void
fnct_GeomCollFromWkb2 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_GEOMETRYCOLLECTION);
}

static void
fnct_LineFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_LINESTRING);
}

static void
fnct_LineFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_LINESTRING);
}

static void
fnct_PointFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_POINT);
}

static void
fnct_PointFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_POINT);
}

static void
fnct_PolyFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_POLYGON);
}

static void
fnct_PolyFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_POLYGON);
}

static void
fnct_MLineFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_MULTILINESTRING);
}

static void
fnct_MLineFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_MULTILINESTRING);
}

static void
fnct_MPointFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_MULTIPOINT);
}

static void
fnct_MPointFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_MULTIPOINT);
}

static void
fnct_MPolyFromWkb1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb1 (context, argc, argv, (short) GAIA_MULTIPOLYGON);
}

static void
fnct_MPolyFromWkb2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    geom_from_wkb2 (context, argc, argv, (short) GAIA_MULTIPOLYGON);
}

static void
fnct_WkbToSql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_WKBToSQL(WKB encoded geometry)
/
/ returns the current geometry by parsing a WKB encoded blob 
/ or NULL if any error is encountered
/
/ the SRID is always 0 [SQL/MM function]
*/
    int len;
    int n_bytes;
    unsigned char *p_result = NULL;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, -1))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_CompressGeometry (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ CompressGeometry(BLOB encoded geometry)
/
/ returns a COMPRESSED geometry [if a valid Geometry was supplied]
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaToCompressedBlobWkb (geo, &p_result, &len);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_UncompressGeometry (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ UncompressGeometry(BLOB encoded geometry)
/
/ returns an UNCOMPRESSED geometry [if a valid Geometry was supplied] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SanitizeGeometry (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ SanitizeGeometry(BLOB encoded geometry)
/
/ returns a SANITIZED geometry [if a valid Geometry was supplied]
/ or NULL in any other case
/
/ Sanitizing includes:
/ - repeated vertices suppression
/ - enforcing ring closure
/
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr sanitized = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  sanitized = gaiaSanitize (geo);
	  gaiaToSpatiaLiteBlobWkbEx2 (sanitized, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
    gaiaFreeGeomColl (sanitized);
}

static void
fnct_EnsureClosedRings (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ EnsureClosedRings(BLOB encoded geometry)
/
/ returns a SANITIZED geometry [if a valid Geometry was supplied]
/ or NULL in any other case
/
/ Sanitizing includes only enforcing ring closure 
/
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr sanitized = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  sanitized = gaiaEnsureClosedRings (geo);
	  gaiaToSpatiaLiteBlobWkbEx2 (sanitized, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
    gaiaFreeGeomColl (sanitized);
}

static void
fnct_RemoveRepeatedPoints (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ RemoveRepeatedPoints(BLOB encoded geometry)
/ RemoveRepeatedPoints(BLOB encoded geometry, double tolerance)
/
/ returns a SANITIZED geometry [if a valid Geometry was supplied]
/ or NULL in any other case
/
/ Sanitizing includes only repeated vertices suppression
/
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr sanitized = NULL;
    double tolerance = 0.0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int tol = sqlite3_value_int (argv[1]);
		tolerance = tol;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  sanitized = gaiaRemoveRepeatedPoints (geo, tolerance);
	  gaiaToSpatiaLiteBlobWkbEx2 (sanitized, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
    gaiaFreeGeomColl (sanitized);
}

static void
cast_count (gaiaGeomCollPtr geom, int *pts, int *lns, int *pgs)
{
/* counting elementary geometries */
    int n_pts = 0;
    int n_lns = 0;
    int n_pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    if (geom)
      {
	  pt = geom->FirstPoint;
	  while (pt)
	    {
		n_pts++;
		pt = pt->Next;
	    }
	  ln = geom->FirstLinestring;
	  while (ln)
	    {
		n_lns++;
		ln = ln->Next;
	    }
	  pg = geom->FirstPolygon;
	  while (pg)
	    {
		n_pgs++;
		pg = pg->Next;
	    }
      }
    *pts = n_pts;
    *lns = n_lns;
    *pgs = n_pgs;
}

static void
fnct_CastAutomagic (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastAutomagic(BLOB encoded geometry)
/
/ accepts on input both a valid SpatiaLite BLOB geometry
/ or a valid GPKG BLOB geometry, thus returning a SpatiaLite
/ BLOB geometry 
/ will return NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		geo = gaiaFromGeoPackageGeometryBlob (p_blob, n_bytes);
		if (geo == NULL)
		    sqlite3_result_null (context);
		else
		  {
		      gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len,
						  gpkg_mode, tiny_point);
		      gaiaFreeGeomColl (geo);
		      sqlite3_result_blob (context, p_result, len, free);
		  }
		return;
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_blob (context, p_result, len, free);
      }
}

static void
fnct_CastToPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToPoint(BLOB encoded geometry)
/
/ returns a POINT-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts == 1 && lns == 0 && pgs == 0)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_POINT;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToLinestring (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ CastToLinestring(BLOB encoded geometry)
/
/ returns a LINESTRING-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts == 0 && lns == 1 && pgs == 0)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_LINESTRING;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToPolygon (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToPolygon(BLOB encoded geometry)
/
/ returns a POLYGON-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts == 0 && lns == 0 && pgs == 1)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_POLYGON;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToMultiPoint (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ CastToMultiPoint(BLOB encoded geometry)
/
/ returns a MULTIPOINT-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts >= 1 && lns == 0 && pgs == 0)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTIPOINT;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToMultiLinestring (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ CastToMultiLinestring(BLOB encoded geometry)
/
/ returns a MULTILINESTRING-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts == 0 && lns >= 1 && pgs == 0)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTILINESTRING;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToMultiPolygon (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ CastToMultiPolygon(BLOB encoded geometry)
/
/ returns a MULTIPOLYGON-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts == 0 && lns == 0 && pgs >= 1)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTIPOLYGON;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToGeometryCollection (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ CastToGeometryCollection(BLOB encoded geometry)
/
/ returns a GEOMETRYCOLLECTION-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts >= 1 || lns >= 1 || pgs >= 1)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_GEOMETRYCOLLECTION;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToMulti (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToMulti(BLOB encoded geometry)
/
/ returns a MULTIPOINT, MULTILINESTRING, MULTIPOLYGON or
/ GEOMETRYCOLLECTION-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts >= 1 || lns >= 1 || pgs >= 1)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		if (pts >= 1 && lns == 0 && pgs == 0)
		    geom2->DeclaredType = GAIA_MULTIPOINT;
		else if (pts == 0 && lns >= 1 && pgs == 0)
		    geom2->DeclaredType = GAIA_MULTILINESTRING;
		else if (pts == 0 && lns == 0 && pgs >= 1)
		    geom2->DeclaredType = GAIA_MULTIPOLYGON;
		else
		    geom2->DeclaredType = GAIA_GEOMETRYCOLLECTION;
		if (geo->DeclaredType == GAIA_GEOMETRYCOLLECTION)
		    geom2->DeclaredType = GAIA_GEOMETRYCOLLECTION;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToSingle (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToSingle(BLOB encoded geometry)
/
/ returns a POINT, LINESTRING or POLYGON-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    int ok;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  ok = 0;
	  if (pts == 1 && lns == 0 && pgs == 0)
	      ok = 1;
	  if (pts == 0 && lns == 1 && pgs == 0)
	      ok = 1;
	  if (pts == 0 && lns == 0 && pgs == 1)
	      ok = 1;
	  if (ok)
	    {
		geom2 = gaiaCloneGeomColl (geo);
		geom2->Srid = geo->Srid;
		if (pts == 1)
		    geom2->DeclaredType = GAIA_POINT;
		else if (lns == 1)
		    geom2->DeclaredType = GAIA_LINESTRING;
		else
		    geom2->DeclaredType = GAIA_POLYGON;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToXY (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToXY(BLOB encoded geometry)
/
/ returns an XY-dimension Geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  geom2 = gaiaCastGeomCollToXY (geo);
	  if (geom2)
	    {
		geom2->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToXYZ (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToXYZ(BLOB encoded geometry)
/    or
/ CastToXYZ(BLOB encoded geometry, nodata double)
/
/ returns an XY-dimension Geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    double no_data;
    int has_no_data = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[1]);
		no_data = val;
		has_no_data = 1;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	    {
		no_data = sqlite3_value_double (argv[1]);
		has_no_data = 1;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (has_no_data)
	      geom2 = gaiaCastGeomCollToXYZnoData (geo, no_data);
	  else
	      geom2 = gaiaCastGeomCollToXYZ (geo);
	  if (geom2)
	    {
		geom2->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToXYM (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToXYM(BLOB encoded geometry)
/    or
/ CastToXYM(BLOB encoded geometry. nodata double)
/
/ returns an XYM-dimension Geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    double no_data;
    int has_no_data = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[1]);
		no_data = val;
		has_no_data = 1;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	    {
		no_data = sqlite3_value_double (argv[1]);
		has_no_data = 1;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (has_no_data)
	      geom2 = gaiaCastGeomCollToXYMnoData (geo, no_data);
	  else
	      geom2 = gaiaCastGeomCollToXYM (geo);
	  if (geom2)
	    {
		geom2->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CastToXYZM (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToXYZM(BLOB encoded geometry)
/    or
/ CastToXYZM(BLOB encoded geometry, nodata_z double, nodata_m double)
/
/ returns an XYZM-dimension Geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    double z_no_data;
    double m_no_data;
    int has_no_data = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[1]);
		z_no_data = val;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      z_no_data = sqlite3_value_double (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[2]);
		m_no_data = val;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      m_no_data = sqlite3_value_double (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  has_no_data = 1;
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (has_no_data)
	      geom2 = gaiaCastGeomCollToXYZMnoData (geo, z_no_data, m_no_data);
	  else
	      geom2 = gaiaCastGeomCollToXYZM (geo);
	  if (geom2)
	    {
		geom2->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ExtractMultiPoint (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ExtractMultiPoint(BLOB encoded geometry)
/
/ returns a MULTIPOINT-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pts >= 1)
	    {
		geom2 = gaiaCloneGeomCollPoints (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTIPOINT;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ExtractMultiLinestring (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ ExtractMultiLinestring(BLOB encoded geometry)
/
/ returns a MULTILINESTRING-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (lns >= 1)
	    {
		geom2 = gaiaCloneGeomCollLinestrings (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTILINESTRING;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ExtractMultiPolygon (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ ExtractMultiPolygon(BLOB encoded geometry)
/
/ returns a MULTIPOLYGON-type geometry [if conversion is possible] 
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    int pts;
    int lns;
    int pgs;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  cast_count (geo, &pts, &lns, &pgs);
	  if (pgs >= 1)
	    {
		geom2 = gaiaCloneGeomCollPolygons (geo);
		geom2->Srid = geo->Srid;
		geom2->DeclaredType = GAIA_MULTIPOLYGON;
		gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
					    tiny_point);
		gaiaFreeGeomColl (geom2);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Reverse (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Reverse(BLOB encoded geometry)
/
/ returns a new Geometry: any Linestring or Ring will be in reverse order
/ or NULL in any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  geom2 = gaiaCloneGeomCollSpecial (geo, GAIA_REVERSE_ORDER);
	  geom2->Srid = geo->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (geom2);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_ForcePolygonCW (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_ForcePolygonCW(BLOB encoded geometry)
/    or
/ ST_ForceLHR(BLOB encoded geometry)
/
/ returns a new Geometry: any Exterior Ring will be in clockwise orientation
/         and any Interior Ring will be in counter-clockwise orientation
/ or NULL on invalid geometries
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  geom2 = gaiaCloneGeomCollSpecial (geo, GAIA_CW_ORDER);
	  geom2->Srid = geo->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (geom2);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_ForcePolygonCCW (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ ST_ForcePolygonCCW(BLOB encoded geometry)
/
/ returns a new Geometry: any Exterior Ring will be in counter-clockwise 
/ orientation and any Interior Ring will be in clockwise orientation
/ or NULL on invalid geometries
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  geom2 = gaiaCloneGeomCollSpecial (geo, GAIA_CCW_ORDER);
	  geom2->Srid = geo->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom2, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (geom2);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_IsPolygonCW (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_IsPolygonCW(BLOB encoded geometry)
/
/ returns TRUE if all Exterior Rings are in clockwise orientation 
/ and all Interior Ring are in counter-clockwise orientation,
/ FALSE if not
/ and -1 on invalid geometries
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  int retval = gaiaCheckClockwise (geo);
	  sqlite3_result_int (context, retval);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_IsPolygonCCW (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_IsPolygonCCW(BLOB encoded geometry)
/
/ returns TRUE if all Exterior Rings are in counter-clockwise 
/ orientation and all Interior Ring are in clockwise orientation,
/ FALSE if not
/ and NULL on invalid geometries
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  int retval = gaiaCheckCounterClockwise (geo);
	  sqlite3_result_int (context, retval);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_Dimension (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Dimension(BLOB encoded geometry)
/
/ returns:
/ 0 if geometry is a POINT or MULTIPOINT
/ 1 if geometry is a LINESTRING or MULTILINESTRING
/ 2 if geometry is a POLYGON or MULTIPOLYGON
/ 0, 1, 2, for GEOMETRYCOLLECTIONS according to geometries contained inside
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int dim;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  dim = gaiaDimension (geo);
	  sqlite3_result_int (context, dim);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CoordDimension (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CoordDimension(BLOB encoded geometry)
/
/ returns:
/ 'XY', 'XYM', 'XYZ', 'XYZM'
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    char *p_dim = NULL;
    char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (geo->DimensionModel == GAIA_XY)
	      p_dim = "XY";
	  else if (geo->DimensionModel == GAIA_XY_Z)
	      p_dim = "XYZ";
	  else if (geo->DimensionModel == GAIA_XY_M)
	      p_dim = "XYM";
	  else if (geo->DimensionModel == GAIA_XY_Z_M)
	      p_dim = "XYZM";
	  if (p_dim)
	    {
		len = strlen (p_dim);
		p_result = malloc (len + 1);
		strcpy (p_result, p_dim);
	    }
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	    {
		len = strlen (p_result);
		sqlite3_result_text (context, p_result, len, free);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NDims (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_NDims(BLOB encoded geometry)
/
/ returns:
/ 2, 3 or 4
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int result = 0;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (geo->DimensionModel == GAIA_XY)
	      result = 2;
	  else if (geo->DimensionModel == GAIA_XY_Z)
	      result = 3;
	  else if (geo->DimensionModel == GAIA_XY_M)
	      result = 3;
	  else if (geo->DimensionModel == GAIA_XY_Z_M)
	      result = 4;
	  sqlite3_result_int (context, result);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_GeometryType (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeometryType(BLOB encoded geometry)
/
/ returns the class for current geometry:
/ 'POINT' or 'MULTIPOINT' [Z, M, ZM]
/ 'LINESTRING' or 'MULTILINESTRING' [Z, M, ZM]
/ 'POLYGON' or 'MULTIPOLYGON' [Z, M, ZM]
/ 'GEOMETRYCOLLECTION'  [Z, M, ZM]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int type;
    char *p_type = NULL;
    char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		char *gpb_type = gaiaGetGeometryTypeFromGPB (p_blob, n_bytes);
		if (gpb_type == NULL)
		    sqlite3_result_null (context);
		else
		  {
		      len = strlen (gpb_type);
		      sqlite3_result_text (context, gpb_type, len, free);
		  }
		return;
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  type = gaiaGeometryType (geo);
	  switch (type)
	    {
	    case GAIA_POINT:
		p_type = "POINT";
		break;
	    case GAIA_POINTZ:
		p_type = "POINT Z";
		break;
	    case GAIA_POINTM:
		p_type = "POINT M";
		break;
	    case GAIA_POINTZM:
		p_type = "POINT ZM";
		break;
	    case GAIA_MULTIPOINT:
		p_type = "MULTIPOINT";
		break;
	    case GAIA_MULTIPOINTZ:
		p_type = "MULTIPOINT Z";
		break;
	    case GAIA_MULTIPOINTM:
		p_type = "MULTIPOINT M";
		break;
	    case GAIA_MULTIPOINTZM:
		p_type = "MULTIPOINT ZM";
		break;
	    case GAIA_LINESTRING:
	    case GAIA_COMPRESSED_LINESTRING:
		p_type = "LINESTRING";
		break;
	    case GAIA_LINESTRINGZ:
	    case GAIA_COMPRESSED_LINESTRINGZ:
		p_type = "LINESTRING Z";
		break;
	    case GAIA_LINESTRINGM:
	    case GAIA_COMPRESSED_LINESTRINGM:
		p_type = "LINESTRING M";
		break;
	    case GAIA_LINESTRINGZM:
	    case GAIA_COMPRESSED_LINESTRINGZM:
		p_type = "LINESTRING ZM";
		break;
	    case GAIA_MULTILINESTRING:
		p_type = "MULTILINESTRING";
		break;
	    case GAIA_MULTILINESTRINGZ:
		p_type = "MULTILINESTRING Z";
		break;
	    case GAIA_MULTILINESTRINGM:
		p_type = "MULTILINESTRING M";
		break;
	    case GAIA_MULTILINESTRINGZM:
		p_type = "MULTILINESTRING ZM";
		break;
	    case GAIA_POLYGON:
	    case GAIA_COMPRESSED_POLYGON:
		p_type = "POLYGON";
		break;
	    case GAIA_POLYGONZ:
	    case GAIA_COMPRESSED_POLYGONZ:
		p_type = "POLYGON Z";
		break;
	    case GAIA_POLYGONM:
	    case GAIA_COMPRESSED_POLYGONM:
		p_type = "POLYGON M";
		break;
	    case GAIA_POLYGONZM:
	    case GAIA_COMPRESSED_POLYGONZM:
		p_type = "POLYGON ZM";
		break;
	    case GAIA_MULTIPOLYGON:
		p_type = "MULTIPOLYGON";
		break;
	    case GAIA_MULTIPOLYGONZ:
		p_type = "MULTIPOLYGON Z";
		break;
	    case GAIA_MULTIPOLYGONM:
		p_type = "MULTIPOLYGON M";
		break;
	    case GAIA_MULTIPOLYGONZM:
		p_type = "MULTIPOLYGON ZM";
		break;
	    case GAIA_GEOMETRYCOLLECTION:
		p_type = "GEOMETRYCOLLECTION";
		break;
	    case GAIA_GEOMETRYCOLLECTIONZ:
		p_type = "GEOMETRYCOLLECTION Z";
		break;
	    case GAIA_GEOMETRYCOLLECTIONM:
		p_type = "GEOMETRYCOLLECTION M";
		break;
	    case GAIA_GEOMETRYCOLLECTIONZM:
		p_type = "GEOMETRYCOLLECTION ZM";
		break;
	    };
	  if (p_type)
	    {
		len = strlen (p_type);
		p_result = malloc (len + 1);
		strcpy (p_result, p_type);
	    }
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	    {
		len = strlen (p_result);
		sqlite3_result_text (context, p_result, len, free);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_GeometryAliasType (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ GeometryAliasType(BLOB encoded geometry)
/
/ returns the alias-class for current geometry:
/ 'POINT'
/ 'LINESTRING'
/ 'POLYGON'
/ 'MULTIPOINT'
/ 'MULTILINESTRING'
/ 'MULTIPOLYGON'
/ 'GEOMETRYCOLLECTION' 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int type;
    char *p_type = NULL;
    char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  type = gaiaGeometryAliasType (geo);
	  switch (type)
	    {
	    case GAIA_POINT:
		p_type = "POINT";
		break;
	    case GAIA_MULTIPOINT:
		p_type = "MULTIPOINT";
		break;
	    case GAIA_LINESTRING:
		p_type = "LINESTRING";
		break;
	    case GAIA_MULTILINESTRING:
		p_type = "MULTILINESTRING";
		break;
	    case GAIA_POLYGON:
		p_type = "POLYGON";
		break;
	    case GAIA_MULTIPOLYGON:
		p_type = "MULTIPOLYGON";
		break;
	    case GAIA_GEOMETRYCOLLECTION:
		p_type = "GEOMETRYCOLLECTION";
		break;
	    };
	  if (p_type)
	    {
		len = strlen (p_type);
		p_result = malloc (len + 1);
		strcpy (p_result, p_type);
	    }
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	    {
		len = strlen (p_result);
		sqlite3_result_text (context, p_result, len, free);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SridFromAuthCRS (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ SridFromAuthCRS(auth_name, auth_srid)
/
/ returns the SRID
/ or NULL if any error is encountered
*/
    const unsigned char *auth_name;
    int auth_srid;
    int srid = -1;
    char *sql;
    char **results;
    int n_rows;
    int n_columns;
    char *err_msg = NULL;
    int ret;
    int i;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    auth_name = sqlite3_value_text (argv[0]);
    auth_srid = sqlite3_value_int (argv[1]);

    sql = sqlite3_mprintf ("SELECT srid FROM spatial_ref_sys "
			   "WHERE Upper(auth_name) = Upper(%Q) AND auth_srid = %d",
			   auth_name, auth_srid);
    ret = sqlite3_get_table (sqlite, sql, &results, &n_rows, &n_columns,
			     &err_msg);
    sqlite3_free (sql);
    if (ret != SQLITE_OK)
	goto done;
    if (n_rows >= 1)
      {
	  for (i = 1; i <= n_rows; i++)
	      srid = atoi (results[(i * n_columns) + 0]);
      }
    sqlite3_free_table (results);
  done:
    sqlite3_result_int (context, srid);
}

static void
fnct_SRID (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Srid(BLOB encoded geometry)
/
/ returns the SRID
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		int srid = gaiaGetSridFromGPB (p_blob, n_bytes);
		sqlite3_result_int (context, srid);
	    }
	  else
	      sqlite3_result_null (context);
	  return;
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	  sqlite3_result_null (context);
      }
    else
	sqlite3_result_int (context, geo->Srid);
    gaiaFreeGeomColl (geo);
}

static void
fnct_SetSRID (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SetSrid(BLOBencoded geometry, srid)
/
/ returns a new geometry that is the original one received, but with the new SRID [no coordinates translation is applied]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    int srid;
    unsigned char *p_result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  geo->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &n_bytes, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, n_bytes, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsEmpty (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsEmpty(BLOB encoded geometry)
/
/ returns:
/ 1 if this geometry contains no elementary geometries
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		int is_empty = gaiaIsEmptyGPB (p_blob, n_bytes);
		sqlite3_result_int (context, is_empty);
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_int (context, -1);
      }
    else
	sqlite3_result_int (context, gaiaIsEmpty (geo));
    gaiaFreeGeomColl (geo);
}

static void
fnct_Is3D (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Is3D(BLOB encoded geometry)
/
/ returns:
/ 1 if this geometry has Z coords
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_int (context, has_z);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_int (context, -1);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_Z
	      || geo->DimensionModel == GAIA_XY_Z_M)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsMeasured (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsMeasured(BLOB encoded geometry)
/
/ returns:
/ 1 if this geometry has M coords
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_int (context, has_m);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_int (context, -1);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_M
	      || geo->DimensionModel == GAIA_XY_Z_M)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_MinZ (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_MinZ(BLOB encoded GEMETRY)
/
/ returns the MinZ coordinate for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double min;
    double max;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      if (has_z)
			  sqlite3_result_double (context, min_z);
		      else
			  sqlite3_result_null (context);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_Z
	      || geo->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaZRangeGeometry (geo, &min, &max);
		sqlite3_result_double (context, min);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_MaxZ (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_MaxZ(BLOB encoded GEMETRY)
/
/ returns the MaxZ coordinate for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double min;
    double max;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      if (has_z)
			  sqlite3_result_double (context, max_z);
		      else
			  sqlite3_result_null (context);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_Z
	      || geo->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaZRangeGeometry (geo, &min, &max);
		sqlite3_result_double (context, max);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_MinM (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_MinM(BLOB encoded GEMETRY)
/
/ returns the MinM coordinate for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double min;
    double max;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      if (has_m)
			  sqlite3_result_double (context, min_m);
		      else
			  sqlite3_result_null (context);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_M
	      || geo->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaMRangeGeometry (geo, &min, &max);
		sqlite3_result_double (context, min);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_MaxM (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_MaxM(BLOB encoded GEMETRY)
/
/ returns the MaxM coordinate for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double min;
    double max;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
    if (!geo)
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      if (has_m)
			  sqlite3_result_double (context, max_m);
		      else
			  sqlite3_result_null (context);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
      {
	  if (geo->DimensionModel == GAIA_XY_M
	      || geo->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaMRangeGeometry (geo, &min, &max);
		sqlite3_result_double (context, max);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_Envelope (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Envelope(BLOB encoded geometry)
/
/ returns the MBR for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr bbox;
    gaiaPolygonPtr polyg;
    gaiaRingPtr rect;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaMbrGeometry (geo);
	  bbox = gaiaAllocGeomColl ();
	  bbox->Srid = geo->Srid;
	  polyg = gaiaAddPolygonToGeomColl (bbox, 5, 0);
	  rect = polyg->Exterior;
	  gaiaSetPoint (rect->Coords, 0, geo->MinX, geo->MinY);	/* vertex # 1 */
	  gaiaSetPoint (rect->Coords, 1, geo->MaxX, geo->MinY);	/* vertex # 2 */
	  gaiaSetPoint (rect->Coords, 2, geo->MaxX, geo->MaxY);	/* vertex # 3 */
	  gaiaSetPoint (rect->Coords, 3, geo->MinX, geo->MaxY);	/* vertex # 4 */
	  gaiaSetPoint (rect->Coords, 4, geo->MinX, geo->MinY);	/* vertex # 5 [same as vertex # 1 to close the polygon] */
	  gaiaToSpatiaLiteBlobWkbEx2 (bbox, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (bbox);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Expand (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Expand(BLOB encoded geometry, double amount)
/
/ returns the MBR for current geometry expanded by "amount" in each direction
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr bbox;
    gaiaPolygonPtr polyg;
    gaiaRingPtr rect;
    double tic;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	tic = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  tic = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaMbrGeometry (geo);
	  bbox = gaiaAllocGeomColl ();
	  bbox->Srid = geo->Srid;
	  polyg = gaiaAddPolygonToGeomColl (bbox, 5, 0);
	  rect = polyg->Exterior;
	  gaiaSetPoint (rect->Coords, 0, geo->MinX - tic, geo->MinY - tic);	/* vertex # 1 */
	  gaiaSetPoint (rect->Coords, 1, geo->MaxX + tic, geo->MinY - tic);	/* vertex # 2 */
	  gaiaSetPoint (rect->Coords, 2, geo->MaxX + tic, geo->MaxY + tic);	/* vertex # 3 */
	  gaiaSetPoint (rect->Coords, 3, geo->MinX - tic, geo->MaxY + tic);	/* vertex # 4 */
	  gaiaSetPoint (rect->Coords, 4, geo->MinX - tic, geo->MinY - tic);	/* vertex # 5 [same as vertex # 1 to close the polygon] */
	  gaiaToSpatiaLiteBlobWkbEx2 (bbox, &p_result, &len, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (bbox);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
build_filter_mbr (sqlite3_context * context, int argc,
		  sqlite3_value ** argv, int mode)
{
/* SQL functions:
/ BuildMbrFilter(double X1, double Y1, double X2, double Y2)
/ FilterMBRWithin(double X1, double Y1, double X2, double Y2)
/ FilterMBRContain(double X1, double Y1, double X2, double Y2)
/ FilterMBRIntersects(double X1, double Y1, double X2, double Y2)
/
/ builds a generic filter for MBR from two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    double x1;
    double y1;
    double x2;
    double y2;
    int int_value;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x1 = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y1 = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x2 = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  x2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y2 = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  y2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaBuildFilterMbr (x1, y1, x2, y2, mode, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

/*
/ the following functions simply readdress the request to build_filter_mbr()
/ setting the appropriate MODe
*/

static void
fnct_BuildMbrFilter (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    build_filter_mbr (context, argc, argv, GAIA_FILTER_MBR_DECLARE);
}

static void
fnct_FilterMbrWithin (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    build_filter_mbr (context, argc, argv, GAIA_FILTER_MBR_WITHIN);
}

static void
fnct_FilterMbrContains (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
    build_filter_mbr (context, argc, argv, GAIA_FILTER_MBR_CONTAINS);
}

static void
fnct_FilterMbrIntersects (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    build_filter_mbr (context, argc, argv, GAIA_FILTER_MBR_INTERSECTS);
}

static void
fnct_BuildMbr1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BuildMBR(double X1, double Y1, double X2, double Y2)
/
/ builds an MBR from two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    double x1;
    double y1;
    double x2;
    double y2;
    int int_value;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x1 = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y1 = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x2 = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  x2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y2 = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  y2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaBuildMbr (x1, y1, x2, y2, -1, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_BuildMbr2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BuildMBR(double X1, double Y1, double X2, double Y2, int SRID)
/
/ builds an MBR from two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    double x1;
    double y1;
    double x2;
    double y2;
    int int_value;
    int srid;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x1 = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y1 = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	x2 = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  x2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	y2 = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  y2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[4]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaBuildMbr (x1, y1, x2, y2, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_BuildCircleMbr1 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BuildCircleMBR(double X, double Y, double radius)
/
/ builds an MBR from two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double radius;
    int int_value;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	radius = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  radius = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaBuildCircleMbr (x, y, radius, -1, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_BuildCircleMbr2 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BuildCircleMBR(double X, double Y, double radius, int SRID)
/
/ builds an MBR from two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    double x;
    double y;
    double radius;
    int int_value;
    int srid;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	radius = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  radius = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[3]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaBuildCircleMbr (x, y, radius, srid, &p_result, &len);
    if (!p_result)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_Extent_step (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Extent(BLOBencoded geom)
/
/ aggregate function - STEP
/
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    double **p;
    double *max_min;
    int *srid_check;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geom)
	return;
    gaiaMbrGeometry (geom);
    p = sqlite3_aggregate_context (context, sizeof (double **));
    if (!(*p))
      {
	  /* this is the first row */
	  max_min = malloc ((sizeof (double) * 5));
	  *(max_min + 0) = geom->MinX;
	  *(max_min + 1) = geom->MinY;
	  *(max_min + 2) = geom->MaxX;
	  *(max_min + 3) = geom->MaxY;
	  srid_check = (int *) (max_min + 4);
	  *(srid_check + 0) = geom->Srid;
	  *(srid_check + 1) = geom->Srid;
	  *p = max_min;
      }
    else
      {
	  /* subsequent rows */
	  max_min = *p;
	  if (geom->MinX < *(max_min + 0))
	      *(max_min + 0) = geom->MinX;
	  if (geom->MinY < *(max_min + 1))
	      *(max_min + 1) = geom->MinY;
	  if (geom->MaxX > *(max_min + 2))
	      *(max_min + 2) = geom->MaxX;
	  if (geom->MaxY > *(max_min + 3))
	      *(max_min + 3) = geom->MaxY;
	  srid_check = (int *) (max_min + 4);
	  if (*(srid_check + 1) != geom->Srid)
	      *(srid_check + 1) = geom->Srid;
      }
    gaiaFreeGeomColl (geom);
}

static void
fnct_Extent_final (sqlite3_context * context)
{
/* SQL function:
/ Extent(BLOBencoded geom)
/
/ aggregate function - FINAL
/
*/
    gaiaGeomCollPtr result;
    gaiaPolygonPtr polyg;
    gaiaRingPtr rect;
    double *max_min;
    double minx;
    double miny;
    double maxx;
    double maxy;
    int *srid_check;
    double **p = sqlite3_aggregate_context (context, 0);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    max_min = *p;
    if (!max_min)
      {
	  sqlite3_result_null (context);
	  return;
      }
    srid_check = (int *) (max_min + 4);
    if (*(srid_check + 0) != *(srid_check + 1))
      {
	  sqlite3_result_null (context);
	  return;
      }
    result = gaiaAllocGeomColl ();
    if (!result)
	sqlite3_result_null (context);
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  result->Srid = *(srid_check + 0);
	  polyg = gaiaAddPolygonToGeomColl (result, 5, 0);
	  rect = polyg->Exterior;
	  minx = *(max_min + 0);
	  miny = *(max_min + 1);
	  maxx = *(max_min + 2);
	  maxy = *(max_min + 3);
	  gaiaSetPoint (rect->Coords, 0, minx, miny);	/* vertex # 1 */
	  gaiaSetPoint (rect->Coords, 1, maxx, miny);	/* vertex # 2 */
	  gaiaSetPoint (rect->Coords, 2, maxx, maxy);	/* vertex # 3 */
	  gaiaSetPoint (rect->Coords, 3, minx, maxy);	/* vertex # 4 */
	  gaiaSetPoint (rect->Coords, 4, minx, miny);	/* vertex # 5 [same as vertex # 1 to close the polygon] */
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
    free (max_min);
}

static void
fnct_MbrMinX (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MbrMinX(BLOB encoded GEMETRY)
/
/ returns the MinX coordinate for current geometry's MBR 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double coord;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!gaiaGetMbrMinX (p_blob, n_bytes, &coord))
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_double (context, min_x);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
	sqlite3_result_double (context, coord);
}

static void
fnct_MbrMaxX (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MbrMaxX(BLOB encoded GEMETRY)
/
/ returns the MaxX coordinate for current geometry's MBR 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double coord;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!gaiaGetMbrMaxX (p_blob, n_bytes, &coord))
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_double (context, max_x);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
	sqlite3_result_double (context, coord);
}

static void
fnct_MbrMinY (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MbrMinY(BLOB encoded GEMETRY)
/
/ returns the MinY coordinate for current geometry's MBR 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double coord;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!gaiaGetMbrMinY (p_blob, n_bytes, &coord))
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_double (context, min_y);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
	sqlite3_result_double (context, coord);
}

static void
fnct_MbrMaxY (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MbrMaxY(BLOB encoded GEMETRY)
/
/ returns the MaxY coordinate for current geometry's MBR 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double coord;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!gaiaGetMbrMaxY (p_blob, n_bytes, &coord))
      {
#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
	  if (gaiaIsValidGPB (p_blob, n_bytes))
	    {
		double min_x;
		double max_x;
		double min_y;
		double max_y;
		int has_z;
		double min_z;
		double max_z;
		int has_m;
		double min_m;
		double max_m;
		if (gaiaGetEnvelopeFromGPB
		    (p_blob, n_bytes, &min_x, &max_x, &min_y, &max_y, &has_z,
		     &min_z, &max_z, &has_m, &min_m, &max_m))
		  {
		      sqlite3_result_double (context, max_y);
		  }
	    }
	  else
#endif /* end GEOPACKAGE: supporting GPKG geometries */
	      sqlite3_result_null (context);
      }
    else
	sqlite3_result_double (context, coord);
}

#ifndef OMIT_GEOCALLBACKS	/* supporting RTree geometry callbacks */
static void
gaia_mbr_del (void *p)
{
/* freeing data used by R*Tree Geometry Callback */
    sqlite3_free (p);
}

static int
fnct_RTreeIntersects (sqlite3_rtree_geometry * p, int nCoord, double *aCoord,
		      int *pRes)
{
/* R*Tree Geometry callback function:
/ ... MATCH RTreeIntersects(double x1, double y1, double x2, double y2)
*/
    struct gaia_rtree_mbr *mbr;
    double xmin;
    double xmax;
    double ymin;
    double ymax;
    float fminx;
    float fminy;
    float fmaxx;
    float fmaxy;
    double tic;
    double tic2;

    if (p->pUser == 0)
      {
	  /* first call: we must check args and then initialize the MBR struct */
	  if (nCoord != 4)
	      return SQLITE_ERROR;
	  if (p->nParam != 4)
	      return SQLITE_ERROR;
	  mbr = (struct gaia_rtree_mbr *) (p->pUser =
					   sqlite3_malloc (sizeof
							   (struct
							    gaia_rtree_mbr)));
	  if (!mbr)
	      return SQLITE_NOMEM;
	  p->xDelUser = gaia_mbr_del;
	  xmin = p->aParam[0];
	  ymin = p->aParam[1];
	  xmax = p->aParam[2];
	  ymax = p->aParam[3];
	  if (xmin > xmax)
	    {
		xmin = p->aParam[2];
		xmax = p->aParam[0];
	    }
	  if (ymin > ymax)
	    {
		ymin = p->aParam[3];
		ymax = p->aParam[1];
	    }

	  /* adjusting the MBR so to compensate for DOUBLE/FLOAT truncations */
	  fminx = (float) xmin;
	  fminy = (float) ymin;
	  fmaxx = (float) xmax;
	  fmaxy = (float) ymax;
	  tic = fabs (xmin - fminx);
	  tic2 = fabs (ymin - fminy);
	  if (tic2 > tic)
	      tic = tic2;
	  tic2 = fabs (xmax - fmaxx);
	  if (tic2 > tic)
	      tic = tic2;
	  tic2 = fabs (ymax - fmaxy);
	  if (tic2 > tic)
	      tic = tic2;
	  tic *= 2.0;

	  mbr->minx = xmin - tic;
	  mbr->miny = ymin - tic;
	  mbr->maxx = xmax + tic;
	  mbr->maxy = ymax + tic;
      }

    mbr = (struct gaia_rtree_mbr *) (p->pUser);
    xmin = aCoord[0];
    xmax = aCoord[1];
    ymin = aCoord[2];
    ymax = aCoord[3];
    *pRes = 1;
/* evaluating Intersects relationship */
    if (xmin > mbr->maxx)
	*pRes = 0;
    if (xmax < mbr->minx)
	*pRes = 0;
    if (ymin > mbr->maxy)
	*pRes = 0;
    if (ymax < mbr->miny)
	*pRes = 0;
    return SQLITE_OK;
}

static int
fnct_RTreeDistWithin (sqlite3_rtree_geometry * p, int nCoord, double *aCoord,
		      int *pRes)
{
/* R*Tree Geometry callback function:
/ ... MATCH RTreeDistWithin(double x, double y, double radius)
*/
    struct gaia_rtree_mbr *mbr;
    double xmin;
    double xmax;
    double ymin;
    double ymax;

    if (p->pUser == 0)
      {
	  /* first call: we must check args and then initialize the MBR struct */
	  if (nCoord != 4)
	      return SQLITE_ERROR;
	  if (p->nParam != 3)
	      return SQLITE_ERROR;
	  mbr = (struct gaia_rtree_mbr *) (p->pUser =
					   sqlite3_malloc (sizeof
							   (struct
							    gaia_rtree_mbr)));
	  if (!mbr)
	      return SQLITE_NOMEM;
	  p->xDelUser = gaia_mbr_del;
	  mbr->minx = p->aParam[0] - p->aParam[2];
	  mbr->miny = p->aParam[1] - p->aParam[2];
	  mbr->maxx = p->aParam[0] + p->aParam[2];
	  mbr->maxy = p->aParam[1] + p->aParam[2];
      }

    mbr = (struct gaia_rtree_mbr *) (p->pUser);
    xmin = aCoord[0];
    xmax = aCoord[1];
    ymin = aCoord[2];
    ymax = aCoord[3];
    *pRes = 1;
/* evaluating Intersects relationship */
    if (xmin > mbr->maxx)
	*pRes = 0;
    if (xmax < mbr->minx)
	*pRes = 0;
    if (ymin > mbr->maxy)
	*pRes = 0;
    if (ymax < mbr->miny)
	*pRes = 0;
    return SQLITE_OK;
}
#endif /* end RTree geometry callbacks */

static void
fnct_X (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ X(BLOB encoded POINT)
/
/ returns the X coordinate for current POINT geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaPointPtr point;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = simplePoint (geo);
	  if (!point)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, point->X);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Y (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Y(BLOB encoded POINT)
/
/ returns the Y coordinate for current POINT geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaPointPtr point;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = simplePoint (geo);
	  if (!point)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, point->Y);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Z (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Z(BLOB encoded POINT)
/
/ returns the Z coordinate for current POINT geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaPointPtr point;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = simplePoint (geo);
	  if (!point)
	      sqlite3_result_null (context);
	  else
	    {
		if (point->DimensionModel == GAIA_XY_Z
		    || point->DimensionModel == GAIA_XY_Z_M)
		    sqlite3_result_double (context, point->Z);
		else
		    sqlite3_result_null (context);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_M (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ M(BLOB encoded POINT)
/
/ returns the M coordinate for current POINT geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaPointPtr point;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = simplePoint (geo);
	  if (!point)
	      sqlite3_result_null (context);
	  else
	    {
		if (point->DimensionModel == GAIA_XY_M
		    || point->DimensionModel == GAIA_XY_Z_M)
		    sqlite3_result_double (context, point->M);
		else
		    sqlite3_result_null (context);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NumPoints (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ NumPoints(BLOB encoded LINESTRING)
/
/ returns the number of vertices for current LINESTRING geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaLinestringPtr line;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  line = simpleLinestring (geo);
	  if (!line)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_int (context, line->Points);
      }
    gaiaFreeGeomColl (geo);
}

static void
point_n (sqlite3_context * context, int argc, sqlite3_value ** argv,
	 int request)
{
/* SQL functions:
/ StartPoint(BLOB encoded LINESTRING geometry)
/ EndPoint(BLOB encoded LINESTRING geometry)
/ PointN(BLOB encoded LINESTRING geometry, integer point_no)
/
/ returns the Nth POINT for current LINESTRING geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int vertex;
    int len;
    double x;
    double y;
    double z;
    double m;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    gaiaLinestringPtr line;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (request == GAIA_POINTN)
      {
	  /* PointN() requires point index to be defined as an SQL function argument */
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  vertex = sqlite3_value_int (argv[1]);
      }
    else if (request == GAIA_END_POINT)
	vertex = -1;		/* EndPoint() specifies a negative point index */
    else
	vertex = 1;		/* StartPoint() */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  line = simpleLinestring (geo);
	  if (!line)
	      sqlite3_result_null (context);
	  else
	    {
		if (vertex < 0)
		    vertex = line->Points - 1;
		else
		    vertex -= 1;	/* decreasing the point index by 1, because PointN counts starting at index 1 */
		if (vertex >= 0 && vertex < line->Points)
		  {
		      if (line->DimensionModel == GAIA_XY_Z)
			{
			    gaiaGetPointXYZ (line->Coords, vertex, &x, &y, &z);
			    result = gaiaAllocGeomCollXYZ ();
			    result->Srid = geo->Srid;
			    gaiaAddPointToGeomCollXYZ (result, x, y, z);
			}
		      else if (line->DimensionModel == GAIA_XY_M)
			{
			    gaiaGetPointXYM (line->Coords, vertex, &x, &y, &m);
			    result = gaiaAllocGeomCollXYM ();
			    result->Srid = geo->Srid;
			    gaiaAddPointToGeomCollXYM (result, x, y, m);
			}
		      else if (line->DimensionModel == GAIA_XY_Z_M)
			{
			    gaiaGetPointXYZM (line->Coords, vertex, &x, &y,
					      &z, &m);
			    result = gaiaAllocGeomCollXYZM ();
			    result->Srid = geo->Srid;
			    gaiaAddPointToGeomCollXYZM (result, x, y, z, m);
			}
		      else
			{
			    gaiaGetPoint (line->Coords, vertex, &x, &y);
			    result = gaiaAllocGeomColl ();
			    result->Srid = geo->Srid;
			    gaiaAddPointToGeomColl (result, x, y);
			}
		  }
		else
		    result = NULL;
		if (!result)
		    sqlite3_result_null (context);
		else
		  {
		      gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
						  gpkg_mode, tiny_point);
		      gaiaFreeGeomColl (result);
		      sqlite3_result_blob (context, p_result, len, free);
		  }
	    }
      }
    gaiaFreeGeomColl (geo);
}

/*
/ the following functions simply readdress the request to point_n()
/ setting the appropriate request mode
*/

static void
fnct_StartPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    point_n (context, argc, argv, GAIA_START_POINT);
}

static void
fnct_EndPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    point_n (context, argc, argv, GAIA_END_POINT);
}

static void
fnct_PointN (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    point_n (context, argc, argv, GAIA_POINTN);
}

static void
fnct_ExteriorRing (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ExteriorRing(BLOB encoded POLYGON geometry)
/
/ returns the EXTERIOR RING for current POLYGON geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int iv;
    double x;
    double y;
    double z;
    double m;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    gaiaPolygonPtr polyg;
    gaiaRingPtr ring;
    gaiaLinestringPtr line;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  polyg = simplePolygon (geo);
	  if (!polyg)
	      sqlite3_result_null (context);
	  else
	    {
		ring = polyg->Exterior;
		if (ring->DimensionModel == GAIA_XY_Z)
		    result = gaiaAllocGeomCollXYZ ();
		else if (ring->DimensionModel == GAIA_XY_M)
		    result = gaiaAllocGeomCollXYM ();
		else if (ring->DimensionModel == GAIA_XY_Z_M)
		    result = gaiaAllocGeomCollXYZM ();
		else
		    result = gaiaAllocGeomColl ();
		result->Srid = geo->Srid;
		line = gaiaAddLinestringToGeomColl (result, ring->Points);
		for (iv = 0; iv < line->Points; iv++)
		  {
		      if (ring->DimensionModel == GAIA_XY_Z)
			{
			    gaiaGetPointXYZ (ring->Coords, iv, &x, &y, &z);
			    gaiaSetPointXYZ (line->Coords, iv, x, y, z);
			}
		      else if (ring->DimensionModel == GAIA_XY_M)
			{
			    gaiaGetPointXYM (ring->Coords, iv, &x, &y, &m);
			    gaiaSetPointXYM (line->Coords, iv, x, y, m);
			}
		      else if (ring->DimensionModel == GAIA_XY_Z_M)
			{
			    gaiaGetPointXYZM (ring->Coords, iv, &x, &y, &z, &m);
			    gaiaSetPointXYZM (line->Coords, iv, x, y, z, m);
			}
		      else
			{
			    gaiaGetPoint (ring->Coords, iv, &x, &y);
			    gaiaSetPoint (line->Coords, iv, x, y);
			}
		  }
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		gaiaFreeGeomColl (result);
		sqlite3_result_blob (context, p_result, len, free);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NumInteriorRings (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ NumInteriorRings(BLOB encoded POLYGON)
/
/ returns the number of INTERIOR RINGS for current POLYGON geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaPolygonPtr polyg;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  polyg = simplePolygon (geo);
	  if (!polyg)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_int (context, polyg->NumInteriors);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_InteriorRingN (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ InteriorRingN(BLOB encoded POLYGON geometry)
/
/ returns the Nth INTERIOR RING for current POLYGON geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int border;
    int iv;
    double x;
    double y;
    double z;
    double m;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    gaiaPolygonPtr polyg;
    gaiaRingPtr ring;
    gaiaLinestringPtr line;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    border = sqlite3_value_int (argv[1]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  polyg = simplePolygon (geo);
	  if (!polyg)
	      sqlite3_result_null (context);
	  else
	    {
		if (border >= 1 && border <= polyg->NumInteriors)
		  {
		      ring = polyg->Interiors + (border - 1);
		      if (ring->DimensionModel == GAIA_XY_Z)
			  result = gaiaAllocGeomCollXYZ ();
		      else if (ring->DimensionModel == GAIA_XY_M)
			  result = gaiaAllocGeomCollXYM ();
		      else if (ring->DimensionModel == GAIA_XY_Z_M)
			  result = gaiaAllocGeomCollXYZM ();
		      else
			  result = gaiaAllocGeomColl ();
		      result->Srid = geo->Srid;
		      line = gaiaAddLinestringToGeomColl (result, ring->Points);
		      for (iv = 0; iv < line->Points; iv++)
			{
			    if (ring->DimensionModel == GAIA_XY_Z)
			      {
				  gaiaGetPointXYZ (ring->Coords, iv, &x,
						   &y, &z);
				  gaiaSetPointXYZ (line->Coords, iv, x, y, z);
			      }
			    else if (ring->DimensionModel == GAIA_XY_M)
			      {
				  gaiaGetPointXYM (ring->Coords, iv, &x,
						   &y, &m);
				  gaiaSetPointXYM (line->Coords, iv, x, y, m);
			      }
			    else if (ring->DimensionModel == GAIA_XY_Z_M)
			      {
				  gaiaGetPointXYZM (ring->Coords, iv, &x,
						    &y, &z, &m);
				  gaiaSetPointXYZM (line->Coords, iv, x,
						    y, z, m);
			      }
			    else
			      {
				  gaiaGetPoint (ring->Coords, iv, &x, &y);
				  gaiaSetPoint (line->Coords, iv, x, y);
			      }
			}
		      gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
						  gpkg_mode, tiny_point);
		      gaiaFreeGeomColl (result);
		      sqlite3_result_blob (context, p_result, len, free);
		  }
		else
		    sqlite3_result_null (context);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NumGeometries (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ NumGeometries(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns the number of elementary geometries for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int cnt = 0;
    gaiaPointPtr point;
    gaiaLinestringPtr line;
    gaiaPolygonPtr polyg;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = geo->FirstPoint;
	  while (point)
	    {
		/* counts how many points are there */
		cnt++;
		point = point->Next;
	    }
	  line = geo->FirstLinestring;
	  while (line)
	    {
		/* counts how many linestrings are there */
		cnt++;
		line = line->Next;
	    }
	  polyg = geo->FirstPolygon;
	  while (polyg)
	    {
		/* counts how many polygons are there */
		cnt++;
		polyg = polyg->Next;
	    }
	  sqlite3_result_int (context, cnt);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NPoints (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_NPoints(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns the total number of points/vertices for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int cnt = 0;
    int ib;
    gaiaPointPtr point;
    gaiaLinestringPtr line;
    gaiaPolygonPtr polyg;
    gaiaRingPtr rng;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = geo->FirstPoint;
	  while (point)
	    {
		/* counts how many points are there */
		cnt++;
		point = point->Next;
	    }
	  line = geo->FirstLinestring;
	  while (line)
	    {
		/* counts how many points are there */
		cnt += line->Points;
		line = line->Next;
	    }
	  polyg = geo->FirstPolygon;
	  while (polyg)
	    {
		/* counts how many points are in the exterior ring */
		rng = polyg->Exterior;
		cnt += rng->Points;
		for (ib = 0; ib < polyg->NumInteriors; ib++)
		  {
		      /* processing any interior ring */
		      rng = polyg->Interiors + ib;
		      cnt += rng->Points;
		  }
		polyg = polyg->Next;
	    }
	  sqlite3_result_int (context, cnt);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NRings (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_NRings(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns the total number of rings for current geometry 
/ (this including both interior and exterior rings)
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int cnt = 0;
    gaiaPolygonPtr polyg;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  polyg = geo->FirstPolygon;
	  while (polyg)
	    {
		/* counts how many rings are there */
		cnt += polyg->NumInteriors + 1;
		polyg = polyg->Next;
	    }
	  sqlite3_result_int (context, cnt);
      }
    gaiaFreeGeomColl (geo);
}

static int
is_single_point (gaiaGeomCollPtr geom)
{
/* check if this geometry is a simple Point */
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts == 1 && lns == 0 && pgs == 0)
	return 1;
    return 0;
}

static int
is_single_linestring (gaiaGeomCollPtr geom)
{
/* check if this geometry is a simple Linestring */
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts == 0 && lns == 1 && pgs == 0)
	return 1;
    return 0;
}

static void
fnct_AddPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_AddPoint(BLOB encoded LINESTRING line, BLOB encoded POINT point)
/ ST_AddPoint(BLOB encoded LINESTRING line, BLOB encoded POINT point, INTEGER position)
/
/ returns a new Linestring by adding a new Point before "position" (zero-based index)
/ a negative "position" (default) means appending to the end 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaLinestringPtr ln;
    gaiaLinestringPtr out_ln;
    gaiaPointPtr pt;
    int position = -1;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr point = NULL;
    gaiaGeomCollPtr out;
    int len;
    unsigned char *p_result = NULL;
    int iv;
    int out_iv;
    double x;
    double y;
    double m;
    double z;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!line)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    point =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!point)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		goto stop;
	    }
	  position = sqlite3_value_int (argv[2]);
      }
    if (is_single_linestring (line) && is_single_point (point))
	;
    else
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    ln = line->FirstLinestring;
    pt = point->FirstPoint;
    if (position >= 0 && position >= ln->Points)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
/* creating the output Geometry */
    if (line->DimensionModel == GAIA_XY_Z)
	out = gaiaAllocGeomCollXYZ ();
    else if (line->DimensionModel == GAIA_XY_M)
	out = gaiaAllocGeomCollXYM ();
    else if (line->DimensionModel == GAIA_XY_Z_M)
	out = gaiaAllocGeomCollXYZM ();
    else
	out = gaiaAllocGeomColl ();
    out->Srid = line->Srid;
    out->DeclaredType = line->DeclaredType;
    out_ln = gaiaAddLinestringToGeomColl (out, ln->Points + 1);
    if (position < 0)
      {
	  /* appending the new Point */
	  for (iv = 0; iv < ln->Points; iv++)
	    {
		if (line->DimensionModel == GAIA_XY_Z)
		  {
		      gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		      gaiaSetPointXYZ (out_ln->Coords, iv, x, y, z);
		  }
		else if (line->DimensionModel == GAIA_XY_M)
		  {
		      gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		      gaiaSetPointXYM (out_ln->Coords, iv, x, y, m);
		  }
		else if (line->DimensionModel == GAIA_XY_Z_M)
		  {
		      gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		      gaiaSetPointXYZM (out_ln->Coords, iv, x, y, z, m);
		  }
		else
		  {
		      gaiaGetPoint (ln->Coords, iv, &x, &y);
		      gaiaSetPoint (out_ln->Coords, iv, x, y);
		  }
	    }
	  /* appending the new Point */
	  x = pt->X;
	  y = pt->Y;
	  z = pt->Z;
	  m = pt->M;
	  out_iv = ln->Points;
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
	    }
	  else
	    {
		gaiaSetPoint (out_ln->Coords, out_iv, x, y);
	    }
      }
    else
      {
	  /* inserting the new Point before "position" */
	  out_iv = 0;
	  for (iv = 0; iv < position; iv++)
	    {
		/* copying all Points before "position" */
		if (line->DimensionModel == GAIA_XY_Z)
		  {
		      gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		      gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
		  }
		else if (line->DimensionModel == GAIA_XY_M)
		  {
		      gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		      gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
		  }
		else if (line->DimensionModel == GAIA_XY_Z_M)
		  {
		      gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		      gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
		  }
		else
		  {
		      gaiaGetPoint (ln->Coords, iv, &x, &y);
		      gaiaSetPoint (out_ln->Coords, out_iv, x, y);
		  }
		out_iv++;
	    }
	  /* inserting the new Point */
	  x = pt->X;
	  y = pt->Y;
	  z = pt->Z;
	  m = pt->M;
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
	    }
	  else
	    {
		gaiaSetPoint (out_ln->Coords, out_iv, x, y);
	    }
	  out_iv++;
	  for (iv = position; iv < ln->Points; iv++)
	    {
		/* copying all Points after "position" */
		if (line->DimensionModel == GAIA_XY_Z)
		  {
		      gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		      gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
		  }
		else if (line->DimensionModel == GAIA_XY_M)
		  {
		      gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		      gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
		  }
		else if (line->DimensionModel == GAIA_XY_Z_M)
		  {
		      gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		      gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
		  }
		else
		  {
		      gaiaGetPoint (ln->Coords, iv, &x, &y);
		      gaiaSetPoint (out_ln->Coords, out_iv, x, y);
		  }
		out_iv++;
	    }
      }
    gaiaToSpatiaLiteBlobWkbEx2 (out, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (out);
    sqlite3_result_blob (context, p_result, len, free);
  stop:
    gaiaFreeGeomColl (line);
    gaiaFreeGeomColl (point);
}

static void
commont_set_point (sqlite3_context * context, gaiaGeomCollPtr line,
		   int position, gaiaGeomCollPtr point)
{
/* SetPoint - common implementation */
    gaiaGeomCollPtr out;
    gaiaLinestringPtr ln;
    gaiaLinestringPtr out_ln;
    gaiaPointPtr pt;
    unsigned char *p_result = NULL;
    int len;
    int iv;
    double x = 0.0;
    double y = 0.0;
    double m = 0.0;
    double z = 0.0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }

    if (is_single_linestring (line) && is_single_point (point))
	;
    else
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    ln = line->FirstLinestring;
    pt = point->FirstPoint;
    if (position < 0 || position >= ln->Points)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
/* creating the output Geometry */
    if (line->DimensionModel == GAIA_XY_Z)
	out = gaiaAllocGeomCollXYZ ();
    else if (line->DimensionModel == GAIA_XY_M)
	out = gaiaAllocGeomCollXYM ();
    else if (line->DimensionModel == GAIA_XY_Z_M)
	out = gaiaAllocGeomCollXYZM ();
    else
	out = gaiaAllocGeomColl ();
    out->Srid = line->Srid;
    out->DeclaredType = line->DeclaredType;
    out_ln = gaiaAddLinestringToGeomColl (out, ln->Points);
    for (iv = 0; iv < ln->Points; iv++)
      {
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
	    }
	  else
	    {
		gaiaGetPoint (ln->Coords, iv, &x, &y);
	    }
	  if (iv == position)
	    {
		/* replacing the new Point */
		x = pt->X;
		y = pt->Y;
		z = pt->Z;
		m = pt->M;
	    }
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaSetPointXYZ (out_ln->Coords, iv, x, y, z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaSetPointXYM (out_ln->Coords, iv, x, y, m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaSetPointXYZM (out_ln->Coords, iv, x, y, z, m);
	    }
	  else
	    {
		gaiaSetPoint (out_ln->Coords, iv, x, y);
	    }
      }
    gaiaToSpatiaLiteBlobWkbEx2 (out, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (out);
    sqlite3_result_blob (context, p_result, len, free);
  stop:
    gaiaFreeGeomColl (line);
    gaiaFreeGeomColl (point);
}

static void
fnct_SetPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_SetPoint(BLOB encoded LINESTRING line, INTEGER position, BLOB encoded POINT point)
/
/ returns a new Linestring by replacing the Point at "position" (zero-based index)
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int position;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr point = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!line)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    position = sqlite3_value_int (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_BLOB)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
    n_bytes = sqlite3_value_bytes (argv[2]);
    point =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!point)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    commont_set_point (context, line, position, point);
}

static void
fnct_SetStartPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_SetStartPoint(BLOB encoded LINESTRING line, BLOB encoded POINT point)
/
/ returns a new Linestring by replacing its StartPoint
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr point = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!line)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    point =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!point)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    commont_set_point (context, line, 0, point);
}

static void
fnct_SetEndPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_SetEndPoint(BLOB encoded LINESTRING line, BLOB encoded POINT point)
/
/ returns a new Linestring by replacing its EndPoint
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaLinestringPtr ln;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr point = NULL;
    int position;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!line)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    point =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!point)
      {
	  gaiaFreeGeomColl (line);
	  sqlite3_result_null (context);
	  return;
      }
    if (is_single_linestring (line) && is_single_point (point))
	;
    else
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    ln = line->FirstLinestring;
    position = ln->Points - 1;
    commont_set_point (context, line, position, point);
    return;
  stop:
    gaiaFreeGeomColl (line);
    gaiaFreeGeomColl (point);
}

static void
fnct_RemovePoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_RemovePoint(BLOB encoded LINESTRING line, INTEGER position)
/
/ returns a new Linestring by removing the Point at "position" (zero-based index)
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaLinestringPtr ln;
    gaiaLinestringPtr out_ln;
    int position;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr out;
    int len;
    unsigned char *p_result = NULL;
    int iv;
    int out_iv;
    double x;
    double y;
    double m;
    double z;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!line)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    position = sqlite3_value_int (argv[1]);
    if (!is_single_linestring (line))
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    ln = line->FirstLinestring;
    if (position < 0 || position >= ln->Points)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
/* creating the output Geometry */
    if (line->DimensionModel == GAIA_XY_Z)
	out = gaiaAllocGeomCollXYZ ();
    else if (line->DimensionModel == GAIA_XY_M)
	out = gaiaAllocGeomCollXYM ();
    else if (line->DimensionModel == GAIA_XY_Z_M)
	out = gaiaAllocGeomCollXYZM ();
    else
	out = gaiaAllocGeomColl ();
    out->Srid = line->Srid;
    out->DeclaredType = line->DeclaredType;
    out_ln = gaiaAddLinestringToGeomColl (out, ln->Points - 1);
    out_iv = 0;
    for (iv = 0; iv < position; iv++)
      {
	  /* copying all Points before "position" */
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
	    }
	  else
	    {
		gaiaGetPoint (ln->Coords, iv, &x, &y);
		gaiaSetPoint (out_ln->Coords, out_iv, x, y);
	    }
	  out_iv++;
      }
    for (iv = position + 1; iv < ln->Points; iv++)
      {
	  /* copying all Points after "position" */
	  if (line->DimensionModel == GAIA_XY_Z)
	    {
		gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		gaiaSetPointXYZ (out_ln->Coords, out_iv, x, y, z);
	    }
	  else if (line->DimensionModel == GAIA_XY_M)
	    {
		gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		gaiaSetPointXYM (out_ln->Coords, out_iv, x, y, m);
	    }
	  else if (line->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		gaiaSetPointXYZM (out_ln->Coords, out_iv, x, y, z, m);
	    }
	  else
	    {
		gaiaGetPoint (ln->Coords, iv, &x, &y);
		gaiaSetPoint (out_ln->Coords, out_iv, x, y);
	    }
	  out_iv++;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (out, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (out);
    sqlite3_result_blob (context, p_result, len, free);
  stop:
    gaiaFreeGeomColl (line);
}

static void
fnct_MakePolygon (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_MakePolygon(BLOB encoded LINESTRING line)
/ ST_MakePolygon(BLOB encoded LINESTRING line, BLOB encoded (MULTI)LINESTING holes)
/
/ returns a new POLYGON from the given exterior and interior rings
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr exterior = NULL;
    gaiaGeomCollPtr interiors = NULL;
    gaiaGeomCollPtr out;
    int len;
    unsigned char *p_result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    exterior =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!exterior)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		goto stop;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
	  n_bytes = sqlite3_value_bytes (argv[1]);
	  interiors =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
	  if (!interiors)
	    {
		sqlite3_result_null (context);
		goto stop;
	    }
      }
    out = gaiaMakePolygon (exterior, interiors);
    if (!out)
      {
	  sqlite3_result_null (context);
	  goto stop;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (out, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (out);
    sqlite3_result_blob (context, p_result, len, free);
  stop:
    gaiaFreeGeomColl (exterior);
    gaiaFreeGeomColl (interiors);
}

static int
getXYZMSinglePoint (gaiaGeomCollPtr geom, double *x, double *y, double *z,
		    double *m)
{
/* check if this geometry is a simple Point (returning full coords) */
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts == 1 && lns == 0 && pgs == 0)
	;
    else
	return 0;
    *x = geom->FirstPoint->X;
    *y = geom->FirstPoint->Y;
    if (geom->DimensionModel == GAIA_XY_Z
	|| geom->DimensionModel == GAIA_XY_Z_M)
	*z = geom->FirstPoint->Z;
    else
	*z = 0.0;
    if (geom->DimensionModel == GAIA_XY_M
	|| geom->DimensionModel == GAIA_XY_Z_M)
	*m = geom->FirstPoint->M;
    else
	*m = 0.0;
    return 1;
}

static void
fnct_SnapToGrid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_SnapToGrid(BLOBencoded geom, double size)
/ ST_SnapToGrid(BLOBencoded geom, double sizeX, double sizeY)
/ ST_SnapToGrid(BLOBencoded geom, double originX, double originY, 
/               double sizeX, double sizeY)
/
/ Snap all points of the input geometry to the grid defined by its 
/ origin and cell size. Remove consecutive points falling on the same
/ cell. Collapsed geometries in a collection are stripped from it.
/
/
/ ST_SnapToGrid(BLOBencoded geom, BLOBencoded point, double sizeX,
/               double sizeY, double sizeZ, double sizeM)
/
/ Snap all points of the input geometry to the grid defined by its 
/ origin (the second argument, must be a point) and cell sizes.
/ 
/ Specify 0 as size for any dimension you don't want to snap to
/ a grid.
/ return NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double origin_x = 0.0;
    double origin_y = 0.0;
    double origin_z = 0.0;
    double origin_m = 0.0;
    double size_x = 0.0;
    double size_y = 0.0;
    double size_z = 0.0;
    double size_m = 0.0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr point = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  /* ST_SnapToGrid(BLOBencoded geom, double size) */
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		size_x = int_value;
		size_y = size_x;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	    {
		size_x = sqlite3_value_double (argv[1]);
		size_y = size_x;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 3)
      {
	  /* ST_SnapToGrid(BLOBencoded geom, double sizeX, double sizeY) */
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		size_x = int_value;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	    {
		size_x = sqlite3_value_double (argv[1]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		size_y = int_value;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	    {
		size_y = sqlite3_value_double (argv[2]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 5)
      {
	  /*
	     / ST_SnapToGrid(BLOBencoded geom, double originX, double originY, 
	     /               double sizeX, double sizeY)
	   */
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		origin_x = int_value;
	    }
	  else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	    {
		origin_x = sqlite3_value_double (argv[1]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		origin_y = int_value;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	    {
		origin_y = sqlite3_value_double (argv[2]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		size_x = int_value;
	    }
	  else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	    {
		size_x = sqlite3_value_double (argv[3]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[4]);
		size_y = int_value;
	    }
	  else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	    {
		size_y = sqlite3_value_double (argv[4]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 6)
      {
	  /*
	     / ST_SnapToGrid(BLOBencoded geom, BLOBencoded point, double sizeX,
	     /               double sizeY, double sizeZ, double sizeM)
	   */
	  if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
	  n_bytes = sqlite3_value_bytes (argv[1]);
	  point =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
	  if (!point)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (!getXYZMSinglePoint
	      (point, &origin_x, &origin_y, &origin_z, &origin_m))
	    {
		gaiaFreeGeomColl (point);
		sqlite3_result_null (context);
		return;
	    }
	  gaiaFreeGeomColl (point);
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		size_x = int_value;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	    {
		size_x = sqlite3_value_double (argv[2]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		size_y = int_value;
	    }
	  else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	    {
		size_y = sqlite3_value_double (argv[3]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[4]);
		size_z = int_value;
	    }
	  else if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	    {
		size_z = sqlite3_value_double (argv[4]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[5]);
		size_m = int_value;
	    }
	  else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	    {
		size_m = sqlite3_value_double (argv[5]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  result =
	      gaiaSnapToGrid (geo, origin_x, origin_y, origin_z, origin_m,
			      size_x, size_y, size_z, size_m);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static char garsMapping[24] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
    'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};

static char
garsLetterCode (int value)
{
    return garsMapping[value];
}

static int
garsMappingIndex (const char letter)
{
    int i = 0;
    for (i = 0; i < 24; ++i)
      {
	  if (letter == garsMapping[i])
	      return i;
      }
    return -1;
}

static double
garsLetterToDegreesLat (char msd, char lsd)
{
    double high = garsMappingIndex (msd) * 24.0;
    double low = garsMappingIndex (lsd);
    if ((high < 0) || (low < 0))
      {
	  return -100.0;
      }
    return (((high + low) * 0.5) - 90.0);
}


static void
fnct_GARSMbr (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GARSMbr(Text)
/
/ converts the Text (which should be a valid GARS area) to the corresponding
/ MBR geometry.
/ This function will return NULL if an error occurs
*/
    const char *text = NULL;
    int len = 0;
    unsigned char *p_result = NULL;
    double x1 = 0.0;
    double y1 = 0.0;
    double x2 = 0.0;
    double y2 = 0.0;

    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = (const char *) sqlite3_value_text (argv[0]);
    if ((strlen (text) < 5) || (strlen (text) > 7))
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (strlen (text) == 5)
      {
	  int numMatch = 0;
	  unsigned int digit100 = 0;
	  char letterMSD = '\0';
	  char letterLSD = '\0';
	  numMatch = sscanf (text, "%u%c%c", &digit100, &letterMSD, &letterLSD);
	  if (numMatch != 3)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x1 = ((digit100 - 1) * 0.5) - 180.0;
	  y1 = garsLetterToDegreesLat (letterMSD, letterLSD);
	  if ((x1 < -180.0) || (x1 > 179.5) || (y1 < -90.0) || (y1 > 89.5))
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x2 = x1 + 0.5;
	  y2 = y1 + 0.5;
      }
    if (strlen (text) == 6)
      {
	  unsigned int numMatch = 0;
	  unsigned int digit100 = 0;
	  char letterMSD = '\0';
	  char letterLSD = '\0';
	  unsigned int digitSegment = 0;
	  numMatch =
	      sscanf (text, "%u%c%c%u", &digit100, &letterMSD, &letterLSD,
		      &digitSegment);
	  if (numMatch != 4)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if ((digitSegment < 1) || (digitSegment > 4))
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x1 = ((digit100 - 1) * 0.5) - 180.0;
	  if ((digitSegment == 2) || (digitSegment == 4))
	    {
		x1 += 0.25;
	    }
	  y1 = garsLetterToDegreesLat (letterMSD, letterLSD);
	  if ((digitSegment == 1) || (digitSegment == 2))
	    {
		y1 += 0.25;
	    }
	  if ((x1 < -180.0) || (x1 > 179.75) || (y1 < -90.0) || (y1 > 89.75))
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x2 = x1 + 0.25;
	  y2 = y1 + 0.25;
      }
    if (strlen (text) == 7)
      {
	  unsigned int numMatch = 0;
	  unsigned int digit100 = 0;
	  char letterMSD = '\0';
	  char letterLSD = '\0';
	  unsigned int digitAndKeypad = 0;
	  unsigned int digitSegment = 0;
	  unsigned int keypadNumber = 0;
	  numMatch =
	      sscanf (text, "%u%c%c%u", &digit100, &letterMSD, &letterLSD,
		      &digitAndKeypad);
	  if (numMatch != 4)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  digitSegment = digitAndKeypad / 10;
	  keypadNumber = digitAndKeypad % 10;
	  if ((digitSegment < 1) || (digitSegment > 4))
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (keypadNumber < 1)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x1 = ((digit100 - 1) * 0.5) - 180.0;
	  if ((digitSegment == 2) || (digitSegment == 4))
	    {
		x1 += 0.25;
	    }
	  y1 = garsLetterToDegreesLat (letterMSD, letterLSD);
	  if ((digitSegment == 1) || (digitSegment == 2))
	    {
		y1 += 0.25;
	    }
	  switch (keypadNumber)
	    {
	    case 1:
		x1 += 0 * 0.25 / 3;
		y1 += 2 * 0.25 / 3;
		break;
	    case 2:
		x1 += 1 * 0.25 / 3;
		y1 += 2 * 0.25 / 3;
		break;
	    case 3:
		x1 += 2 * 0.25 / 3;
		y1 += 2 * 0.25 / 3;
		break;
	    case 4:
		x1 += 0 * 0.25 / 3;
		y1 += 1 * 0.25 / 3;
		break;
	    case 5:
		x1 += 1 * 0.25 / 3;
		y1 += 1 * 0.25 / 3;
		break;
	    case 6:
		x1 += 2 * 0.25 / 3;
		y1 += 1 * 0.25 / 3;
		break;
	    case 7:
		x1 += 0 * 0.25 / 3;
		y1 += 0 * 0.25 / 3;
		break;
	    case 8:
		x1 += 1 * 0.25 / 3;
		y1 += 0 * 0.25 / 3;
		break;
	    case 9:
		x1 += 2 * 0.25 / 3;
		y1 += 0 * 0.25 / 3;
		break;
	    }
	  if ((x1 < -180.0) || (x1 >= 180.0) || (y1 < -90.0) || (y1 >= 90.0))
	    {
		sqlite3_result_null (context);
		return;
	    }
	  x2 = x1 + (0.25 / 3);
	  y2 = y1 + (0.25 / 3);
      }
    gaiaBuildMbr (x1, y1, x2, y2, 4326, &p_result, &len);
    if (!p_result)
      {
	  sqlite3_result_null (context);
	  spatialite_e ("bad p_result\n");
      }
    else
	sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_ToGARS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ToGARS(BLOB encoded POINT)
/
/ returns the Global Area Reference System coordinate area for a given point,
/ or NULL if an error occurs
*/
    unsigned char *p_blob;
    int n_bytes;
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr point;
    gaiaLinestringPtr line;
    gaiaPolygonPtr polyg;
    gaiaGeomCollPtr geo = NULL;
    char p_result[8];
    int lon_band = 0;
    double lon_minutes = 0;
    int segmentNumber = 0;
    int lat_band = 0;
    double lat_minutes = 0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaNormalizeLonLat (geo);
    point = geo->FirstPoint;
    while (point != NULL)
      {
	  pts++;
	  point = point->Next;
      }
    line = geo->FirstLinestring;
    while (line != NULL)
      {
	  lns++;
	  line = line->Next;
      }
    polyg = geo->FirstPolygon;
    while (polyg != NULL)
      {
	  pgs++;
	  polyg = polyg->Next;
      }
    if (pts == 1 && lns == 0 && pgs == 0)
	point = geo->FirstPoint;
    else
      {
	  /* not a single Point */
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    /* longitude band */
    lon_band = 1 + (int) ((point->X + 180.0) * 2);
    sprintf (p_result, "%03i", lon_band);
    /* latitude band */
    lat_band = (int) ((point->Y + 90.0) * 2);
    p_result[3] = garsLetterCode (lat_band / 24);
    p_result[4] = garsLetterCode (lat_band % 24);
    /* quadrant */
    lon_minutes = fmod ((point->X + 180.0), 0.5) * 60.0;
    if (lon_minutes < 15.0)
      {
	  segmentNumber = 1;
      }
    else
      {
	  segmentNumber = 2;
	  lon_minutes -= 15.0;
      }
    lat_minutes = fmod ((point->Y + 90.0), 0.5) * 60.0;
    if (lat_minutes < 15.0)
      {
	  segmentNumber += 2;
      }
    else
      {
	  /* we already have the right segment */
	  lat_minutes -= 15.0;
      }
    sprintf (&(p_result[5]), "%i", segmentNumber);
    /* area */
    segmentNumber = 0;
    if (lon_minutes >= 10.0)
      {
	  segmentNumber = 3;
      }
    else if (lon_minutes >= 5.0)
      {
	  segmentNumber = 2;
      }
    else
      {
	  segmentNumber = 1;
      }
    if (lat_minutes >= 10.0)
      {
	  /* nothing to add */
      }
    else if (lat_minutes >= 5.0)
      {
	  segmentNumber += 3;
      }
    else
      {
	  segmentNumber += 6;
      }
    sprintf (&(p_result[6]), "%i", segmentNumber);
    sqlite3_result_text (context, p_result, 7, SQLITE_TRANSIENT);
    gaiaFreeGeomColl (geo);
}

static void
fnct_GeometryN (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeometryN(BLOB encoded GEOMETRYCOLLECTION geometry)
/
/ returns the Nth geometry for current GEOMETRYCOLLECTION or MULTIxxxx geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int entity;
    int len;
    int cnt = 0;
    int iv;
    int ib;
    double x;
    double y;
    double z;
    double m;
    gaiaPointPtr point;
    gaiaLinestringPtr line;
    gaiaLinestringPtr line2;
    gaiaPolygonPtr polyg;
    gaiaPolygonPtr polyg2;
    gaiaRingPtr ring_in;
    gaiaRingPtr ring_out;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    entity = sqlite3_value_int (argv[1]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  point = geo->FirstPoint;
	  while (point)
	    {
		/* counts how many points are there */
		cnt++;
		if (cnt == entity)
		  {
		      /* ok, required elementary geometry is this POINT */
		      if (point->DimensionModel == GAIA_XY_Z)
			  result = gaiaAllocGeomCollXYZ ();
		      else if (point->DimensionModel == GAIA_XY_M)
			  result = gaiaAllocGeomCollXYM ();
		      else if (point->DimensionModel == GAIA_XY_Z_M)
			  result = gaiaAllocGeomCollXYZM ();
		      else
			  result = gaiaAllocGeomColl ();
		      result->Srid = geo->Srid;
		      if (point->DimensionModel == GAIA_XY_Z)
			  gaiaAddPointToGeomCollXYZ (result, point->X,
						     point->Y, point->Z);
		      else if (point->DimensionModel == GAIA_XY_M)
			  gaiaAddPointToGeomCollXYM (result, point->X,
						     point->Y, point->M);
		      else if (point->DimensionModel == GAIA_XY_Z_M)
			  gaiaAddPointToGeomCollXYZM (result, point->X,
						      point->Y, point->Z,
						      point->M);
		      else
			  gaiaAddPointToGeomColl (result, point->X, point->Y);
		      goto skip;
		  }
		point = point->Next;
	    }
	  line = geo->FirstLinestring;
	  while (line)
	    {
		/* counts how many linestrings are there */
		cnt++;
		if (cnt == entity)
		  {
		      /* ok, required elementary geometry is this LINESTRING */
		      if (line->DimensionModel == GAIA_XY_Z)
			  result = gaiaAllocGeomCollXYZ ();
		      else if (line->DimensionModel == GAIA_XY_M)
			  result = gaiaAllocGeomCollXYM ();
		      else if (line->DimensionModel == GAIA_XY_Z_M)
			  result = gaiaAllocGeomCollXYZM ();
		      else
			  result = gaiaAllocGeomColl ();
		      result->Srid = geo->Srid;
		      line2 =
			  gaiaAddLinestringToGeomColl (result, line->Points);
		      for (iv = 0; iv < line2->Points; iv++)
			{
			    if (line->DimensionModel == GAIA_XY_Z)
			      {
				  gaiaGetPointXYZ (line->Coords, iv, &x,
						   &y, &z);
				  gaiaSetPointXYZ (line2->Coords, iv, x, y, z);
			      }
			    else if (line->DimensionModel == GAIA_XY_M)
			      {
				  gaiaGetPointXYM (line->Coords, iv, &x,
						   &y, &m);
				  gaiaSetPointXYM (line2->Coords, iv, x, y, m);
			      }
			    else if (line->DimensionModel == GAIA_XY_Z_M)
			      {
				  gaiaGetPointXYZM (line->Coords, iv, &x,
						    &y, &z, &m);
				  gaiaSetPointXYZM (line2->Coords, iv, x,
						    y, z, m);
			      }
			    else
			      {
				  gaiaGetPoint (line->Coords, iv, &x, &y);
				  gaiaSetPoint (line2->Coords, iv, x, y);
			      }
			}
		      goto skip;
		  }
		line = line->Next;
	    }
	  polyg = geo->FirstPolygon;
	  while (polyg)
	    {
		/* counts how many polygons are there */
		cnt++;
		if (cnt == entity)
		  {
		      /* ok, required elementary geometry is this POLYGON */
		      if (polyg->DimensionModel == GAIA_XY_Z)
			  result = gaiaAllocGeomCollXYZ ();
		      else if (polyg->DimensionModel == GAIA_XY_M)
			  result = gaiaAllocGeomCollXYM ();
		      else if (polyg->DimensionModel == GAIA_XY_Z_M)
			  result = gaiaAllocGeomCollXYZM ();
		      else
			  result = gaiaAllocGeomColl ();
		      result->Srid = geo->Srid;
		      ring_in = polyg->Exterior;
		      polyg2 =
			  gaiaAddPolygonToGeomColl (result,
						    ring_in->Points,
						    polyg->NumInteriors);
		      ring_out = polyg2->Exterior;
		      for (iv = 0; iv < ring_out->Points; iv++)
			{
			    /* copying the exterior ring POINTs */
			    if (ring_in->DimensionModel == GAIA_XY_Z)
			      {
				  gaiaGetPointXYZ (ring_in->Coords, iv,
						   &x, &y, &z);
				  gaiaSetPointXYZ (ring_out->Coords, iv,
						   x, y, z);
			      }
			    else if (ring_in->DimensionModel == GAIA_XY_M)
			      {
				  gaiaGetPointXYM (ring_in->Coords, iv,
						   &x, &y, &m);
				  gaiaSetPointXYM (ring_out->Coords, iv,
						   x, y, m);
			      }
			    else if (ring_in->DimensionModel == GAIA_XY_Z_M)
			      {
				  gaiaGetPointXYZM (ring_in->Coords, iv,
						    &x, &y, &z, &m);
				  gaiaSetPointXYZM (ring_out->Coords, iv,
						    x, y, z, m);
			      }
			    else
			      {
				  gaiaGetPoint (ring_in->Coords, iv, &x, &y);
				  gaiaSetPoint (ring_out->Coords, iv, x, y);
			      }
			}
		      for (ib = 0; ib < polyg2->NumInteriors; ib++)
			{
			    /* processing the interior rings */
			    ring_in = polyg->Interiors + ib;
			    ring_out =
				gaiaAddInteriorRing (polyg2, ib,
						     ring_in->Points);
			    for (iv = 0; iv < ring_out->Points; iv++)
			      {
				  if (ring_in->DimensionModel == GAIA_XY_Z)
				    {
					gaiaGetPointXYZ (ring_in->Coords,
							 iv, &x, &y, &z);
					gaiaSetPointXYZ (ring_out->Coords, iv,
							 x, y, z);
				    }
				  else if (ring_in->DimensionModel == GAIA_XY_M)
				    {
					gaiaGetPointXYM (ring_in->Coords,
							 iv, &x, &y, &m);
					gaiaSetPointXYM (ring_out->Coords, iv,
							 x, y, m);
				    }
				  else if (ring_in->DimensionModel ==
					   GAIA_XY_Z_M)
				    {
					gaiaGetPointXYZM (ring_in->Coords, iv,
							  &x, &y, &z, &m);
					gaiaSetPointXYZM (ring_out->Coords,
							  iv, x, y, z, m);
				    }
				  else
				    {
					gaiaGetPoint (ring_in->Coords,
						      iv, &x, &y);
					gaiaSetPoint (ring_out->Coords,
						      iv, x, y);
				    }
			      }
			}
		      goto skip;
		  }
		polyg = polyg->Next;
	    }
	skip:
	  if (result)
	    {
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		gaiaFreeGeomColl (result);
		sqlite3_result_blob (context, p_result, len, free);
	    }
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo);
}

static void
mbrs_eval (sqlite3_context * context, int argc, sqlite3_value ** argv,
	   int request)
{
/* SQL function:
/ MBRsomething(BLOB encoded GEOMETRY-1, BLOB encoded GEOMETRY-2)
/
/ returns:
/ 1 if the required spatial relationship between the two MBRs is TRUE
/ 0 otherwise
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 = gaiaFromSpatiaLiteBlobMbr (p_blob, n_bytes);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 = gaiaFromSpatiaLiteBlobMbr (p_blob, n_bytes);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  ret = 0;
	  gaiaMbrGeometry (geo1);
	  gaiaMbrGeometry (geo2);
	  switch (request)
	    {
	    case GAIA_MBR_CONTAINS:
		ret = gaiaMbrsContains (geo1, geo2);
		break;
	    case GAIA_MBR_DISJOINT:
		ret = gaiaMbrsDisjoint (geo1, geo2);
		break;
	    case GAIA_MBR_EQUAL:
		ret = gaiaMbrsEqual (geo1, geo2);
		break;
	    case GAIA_MBR_INTERSECTS:
		ret = gaiaMbrsIntersects (geo1, geo2);
		break;
	    case GAIA_MBR_OVERLAPS:
		ret = gaiaMbrsOverlaps (geo1, geo2);
		break;
	    case GAIA_MBR_TOUCHES:
		ret = gaiaMbrsTouches (geo1, geo2);
		break;
	    case GAIA_MBR_WITHIN:
		ret = gaiaMbrsWithin (geo1, geo2);
		break;
	    }
	  if (ret < 0)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

/*
/ the following functions simply readdress the mbr_eval()
/ setting the appropriate request mode
*/

static void
fnct_MbrContains (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_CONTAINS);
}

static void
fnct_MbrDisjoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_DISJOINT);
}

static void
fnct_MbrEqual (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_EQUAL);
}

static void
fnct_MbrIntersects (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_INTERSECTS);
}

static void
fnct_EnvIntersects (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_EnvIntersects(Geometry geom, double X1, double Y1, double X2, double Y2)
/ ST_EnvelopesIntersects(Geometry geom, double X1, double Y1, double X2, double Y2)
/
/ the second MBR is defined by two points (identifying a rectangle's diagonal) 
/ or NULL if any error is encountered
*/
    double x1;
    double y1;
    double x2;
    double y2;
    int int_value;
    unsigned char *p_blob;
    int n_bytes;
    int ret = 0;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaLinestringPtr ln;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	x1 = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  x1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	y1 = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  y1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	x2 = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  x2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	y2 = sqlite3_value_double (argv[4]);
    else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[4]);
	  y2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1)
	sqlite3_result_null (context);
    else
      {
	  gaiaMbrGeometry (geo1);
	  geo2 = gaiaAllocGeomColl ();
	  ln = gaiaAddLinestringToGeomColl (geo2, 2);
	  gaiaSetPoint (ln->Coords, 0, x1, y1);
	  gaiaSetPoint (ln->Coords, 1, x2, y2);
	  gaiaMbrGeometry (geo2);
	  ret = gaiaMbrsIntersects (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}


static void
fnct_MbrOverlaps (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_OVERLAPS);
}

static void
fnct_MbrTouches (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_TOUCHES);
}

static void
fnct_MbrWithin (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    mbrs_eval (context, argc, argv, GAIA_MBR_WITHIN);
}

static void
fnct_ShiftCoords (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ShiftCoords(BLOBencoded geometry, shiftX, shiftY)
/
/ returns a new geometry that is the original one received, but with shifted coordinates
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    double shift_x;
    double shift_y;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	shift_x = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  shift_x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	shift_y = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  shift_y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaShiftCoords (geo, shift_x, shift_y);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Translate (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Translate(BLOBencoded geometry, shiftX, shiftY, shiftZ)
/
/ returns a new geometry that is the original one received, but with shifted coordinates
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    double shift_x;
    double shift_y;
    double shift_z;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	shift_x = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  shift_x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	shift_y = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  shift_y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	shift_z = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[3]);
	  shift_z = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaShiftCoords3D (geo, shift_x, shift_y, shift_z);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}


static void
fnct_ShiftLongitude (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ShiftLongitude(BLOBencoded geometry)
/
/ returns a new geometry that is the original one received, but with negative
/ longitudes shifted by 360
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaShiftLongitude (geo);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_NormalizeLonLat (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ NormalizeLonLat (BLOBencoded geometry)
/
/ returns a new geometry that is the original one received, but with longitude
/ and latitude values shifted into the range [-180 - 180, -90 - 90]. 
/ NULL is returned if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaNormalizeLonLat (geo);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ScaleCoords (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ScaleCoords(BLOBencoded geometry, scale_factor_x [, scale_factor_y])
/
/ returns a new geometry that is the original one received, but with scaled coordinates
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    double scale_x;
    double scale_y;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	scale_x = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  scale_x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
	scale_y = scale_x;	/* this one is an isotropic scaling request */
    else
      {
	  /* an anisotropic scaling is requested */
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      scale_y = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		scale_y = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaScaleCoords (geo, scale_x, scale_y);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_RotateCoords (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ RotateCoords(BLOBencoded geometry, angle)
/
/ returns a new geometry that is the original one received, but with rotated coordinates
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    double angle;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaRotateCoords (geo, angle);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ReflectCoords (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ReflectCoords(BLOBencoded geometry, x_axis,  y_axis)
/
/ returns a new geometry that is the original one received, but with mirrored coordinates
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int x_axis;
    int y_axis;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	x_axis = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	y_axis = sqlite3_value_int (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaReflectCoords (geo, x_axis, y_axis);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SwapCoords (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SwapCoords(BLOBencoded geometry)
/
/ returns a new geometry that is the original one received, but with swapped x- and y-coordinate
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaSwapCoords (geo);
	  gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode,
				      tiny_point);
	  if (!p_result)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (geo);
}

SPATIALITE_PRIVATE int
getEllipsoidParams (void *p_sqlite, int srid, double *a, double *b, double *rf)
{
/* 
/ retrieves the PROJ +ellps=xx [+a=xx +b=xx] params 
/from SPATIAL_SYS_REF table, if possible 
*/
    sqlite3 *sqlite = (sqlite3 *) p_sqlite;
    char *proj4text;
    char *p_proj;
    char *p_ellps;
    char *p_datum;
    char *p_a;
    char *p_b;
    char *p_end;

    if (srid == 0)
      {
	  /* 
	     / SRID=0 is formally defined as "Undefined Geographic"
	     / so will default to SRID=4326 (WGS84 Long/Lat)
	   */
	  srid = 4326;
      }
    getProjParams (sqlite, srid, &proj4text);
    if (proj4text == NULL)
	return 0;
/* parsing the proj4text geodesic string */
    p_proj = strstr (proj4text, "+proj=");
    p_datum = strstr (proj4text, "+datum=");
    p_ellps = strstr (proj4text, "+ellps=");
    p_a = strstr (proj4text, "+a=");
    p_b = strstr (proj4text, "+b=");
/* checking if +proj=longlat is true */
    if (!p_proj)
	goto invalid;
    p_end = strchr (p_proj, ' ');
    if (p_end)
	*p_end = '\0';
    if (strcmp (p_proj + 6, "longlat") != 0)
	goto invalid;
    if (p_ellps)
      {
	  /* trying to retrieve the ellipsoid params by name */
	  p_end = strchr (p_ellps, ' ');
	  if (p_end)
	      *p_end = '\0';
	  if (gaiaEllipseParams (p_ellps + 7, a, b, rf))
	      goto valid;
      }
    else if (p_datum)
      {
	  /*
	     / starting since GDAL 1.9.0 the WGS84 [4326] PROJ.4 def doesn't 
	     / declares any longer the "+ellps=" param
	     / in this case we'll attempt to recover using "+datum=".
	   */
	  p_end = strchr (p_datum, ' ');
	  if (p_end)
	      *p_end = '\0';
	  if (gaiaEllipseParams (p_datum + 7, a, b, rf))
	      goto valid;
      }
    if (p_a && p_b)
      {
	  /* trying to retrieve the +a=xx and +b=xx args */
	  p_end = strchr (p_a, ' ');
	  if (p_end)
	      *p_end = '\0';
	  p_end = strchr (p_b, ' ');
	  if (p_end)
	      *p_end = '\0';
	  *a = atof (p_a + 3);
	  *b = atof (p_b + 3);
	  *rf = 1.0 / ((*a - *b) / *a);
	  goto valid;
      }

  valid:
    free (proj4text);
    return 1;

  invalid:
    free (proj4text);
    return 0;
}

static void
fnct_FromEWKB (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromEWKB(EWKB encoded geometry)
/
/ returns the current geometry by parsing Geos/PostGis EWKB encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaFromEWKB (text);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_ToEWKB (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsEWKB(BLOB encoded geometry)
/
/ returns a text string corresponding to Geos/PostGIS EWKB notation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  gaiaOutBufferInitialize (&out_buf);
	  gaiaToEWKB (&out_buf, geo);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_ToEWKT (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsEWKT(BLOB encoded geometry)
/
/ returns the corresponding PostGIS EWKT encoded value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaOutBuffer out_buf;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    gaiaOutBufferInitialize (&out_buf);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  gaiaToEWKT (&out_buf, geo);
	  if (out_buf.Error || out_buf.Buffer == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		len = out_buf.WriteOffset;
		sqlite3_result_text (context, out_buf.Buffer, len, free);
		out_buf.Buffer = NULL;
	    }
      }
    gaiaFreeGeomColl (geo);
    gaiaOutBufferReset (&out_buf);
}

static void
fnct_FromEWKT (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromEWKT(EWKT encoded geometry)
/
/ returns the current geometry by parsing EWKT  (PostGIS) encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseEWKT (text);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_FromGeoJSON (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromGeoJSON(GeoJSON encoded geometry)
/
/ returns the current geometry by parsing GeoJSON encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseGeoJSON (text);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_FromKml (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromKml(KML encoded geometry)
/
/ returns the current geometry by parsing KML encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseKml (text);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_FromGml (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromGml(GML encoded geometry)
/
/ returns the current geometry by parsing GML encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    void *data = sqlite3_user_data (context);
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    if (data != NULL)
	geo = gaiaParseGml_r (data, text, sqlite);
    else
	geo = gaiaParseGml (text, sqlite);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_LinesFromRings (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LinesFromRings(BLOBencoded geometry, BOOL multi_linestring)
/
/ returns a new geometry [LINESTRING or MULTILINESTRING] representing 
/ the linearization for current (MULTI)POLYGON geometry
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr geom_new = NULL;
    int len;
    int multi_linestring = 0;
    unsigned char *p_result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      multi_linestring = sqlite3_value_int (argv[1]);
      }
    geom_new = gaiaLinearize (geo, multi_linestring);
    if (!geom_new)
	goto invalid;
    gaiaFreeGeomColl (geo);
    gaiaToSpatiaLiteBlobWkbEx2 (geom_new, &p_result, &len, gpkg_mode,
				tiny_point);
    gaiaFreeGeomColl (geom_new);
    sqlite3_result_blob (context, p_result, len, free);
    return;
  invalid:
    if (geo)
	gaiaFreeGeomColl (geo);
    sqlite3_result_null (context);
}

#ifndef OMIT_GEOS		/* including GEOS */

static void
fnct_BuildArea (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BuildArea(BLOBencoded geometry)
/
/ Assuming that Geometry represents a set of sparse Linestrings,
/ this function will attempt to reassemble a single Polygon
/ (or a set of Polygons)
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaPolygonize_r (data, geo, 0);
	  else
	      result = gaiaPolygonize (geo, 0);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}


static void
fnct_Polygonize_step (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ Polygonize(BLOBencoded geom)
/
/ aggregate function - STEP
/
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    gaiaGeomCollPtr result;
    gaiaGeomCollPtr *p;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geom)
	return;
    p = sqlite3_aggregate_context (context, sizeof (gaiaGeomCollPtr));
    if (!(*p))
      {
	  /* this is the first row */
	  *p = geom;
      }
    else
      {
	  /* subsequent rows */
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaMergeGeometries_r (data, *p, geom);
	  else
	      result = gaiaMergeGeometries (*p, geom);
	  *p = result;
	  gaiaFreeGeomColl (geom);
      }
}

static void
fnct_Polygonize_final (sqlite3_context * context)
{
/* SQL function:
/ Polygonize(BLOBencoded geom)
/
/ aggregate function - FINAL
/
*/
    gaiaGeomCollPtr result;
    gaiaGeomCollPtr geom;
    gaiaGeomCollPtr *p = sqlite3_aggregate_context (context, 0);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    result = *p;
    if (!result)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      geom = gaiaPolygonize_r (data, result, 0);
	  else
	      geom = gaiaPolygonize (result, 0);
	  if (geom == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		geom->Srid = result->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_result, &len, gpkg_mode,
					    tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (geom);
	    }
	  gaiaFreeGeomColl (result);
      }
}

#endif /* end including GEOS */

static void
fnct_DissolveSegments (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ DissolveSegments(BLOBencoded geometry)
/
/ Dissolves any LINESTRING or RING into elementary segments
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaDissolveSegments (geo);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_DissolvePoints (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ DissolvePoints(BLOBencoded geometry)
/
/ Dissolves any LINESTRING or RING into elementary Vertices
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaDissolvePoints (geo);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_CollectionExtract (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ CollectionExtract(BLOBencoded geometry, Integer type)
/
/ Extracts from a GEOMETRYCOLLECTION any item of the required TYPE
/ 1=Point - 2=Linestring - 3=Polygon
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    int type;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	type = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (type == 1 || type == 2 || type == 3)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  switch (type)
	    {
	    case 1:
		result = gaiaExtractPointsFromGeomColl (geo);
		break;
	    case 2:
		result = gaiaExtractLinestringsFromGeomColl (geo);
		break;
	    case 3:
		result = gaiaExtractPolygonsFromGeomColl (geo);
		break;
	    };
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_AddMeasure (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL functions:
/ ST_AddMeasure(BLOBencoded geometry, Double m_start, Double m_end)
/
/ Will return a new GEOMETRY (supporting M) with measures linearly
/ interpolated between the start and end points.
/ the input Geometry is expected to be a Linestring or MultiLinestring
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    double m_start;
    double m_end;
    int intval;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	m_start = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  intval = sqlite3_value_int (argv[1]);
	  m_start = intval;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	m_end = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  intval = sqlite3_value_int (argv[2]);
	  m_end = intval;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaAddMeasure (geo, m_start, m_end);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_InterpolatePoint (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL functions:
/ ST_InterpolatePoint(BLOBencoded line-geometry, BLOBencoded point-geometry)
/
/ Will return the M-value at the interpolated point nearest to the given point
/ the first input Geometry is expected to be a Linestring supporting M,
/ the second input Geometry is expected to be a Point
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob1;
    int n_bytes1;
    unsigned char *p_blob2;
    int n_bytes2;
    gaiaGeomCollPtr line = NULL;
    gaiaGeomCollPtr point = NULL;
    double m_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes1 = sqlite3_value_bytes (argv[0]);
    line =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob1, n_bytes1, gpkg_mode,
				     gpkg_amphibious);
    p_blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes2 = sqlite3_value_bytes (argv[1]);
    point =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob2, n_bytes2, gpkg_mode,
				     gpkg_amphibious);
    if (line == NULL || point == NULL)
	sqlite3_result_null (context);
    else
      {
	  if (!gaiaInterpolatePoint (cache, line, point, &m_value))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, m_value);
      }
    if (line != NULL)
	gaiaFreeGeomColl (line);
    if (point != NULL)
	gaiaFreeGeomColl (point);
}

static void
fnct_LocateBetweenMeasures (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL functions:
/ ST_Locate_Along_Measure(BLOBencoded geometry, Double m_value)
/ ST_LocateAlong(BLOBencoded geometry, Double m_value)
/ ST_Locate_Between_Measures(BLOBencoded geometry, Double m_start, Double m_end)
/ ST_LocateBetween(BLOBencoded geometry, Double m_start, Double m_end)
/
/ Extracts from a GEOMETRY (supporting M) any Point/Linestring
/ matching the range of measures
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    double m_start;
    double m_end;
    int intval;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	m_start = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  intval = sqlite3_value_int (argv[1]);
	  m_start = intval;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 2)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      m_end = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		intval = sqlite3_value_int (argv[2]);
		m_end = intval;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else
	m_end = m_start;
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaLocateBetweenMeasures (geo, m_start, m_end);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsValidTrajectory (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ST_IsValidTrajectory(BLOBencoded geometry)
/
/ returns:
/ 1 if this GEOMETRY is a LINESTRING supporting M-values and
/   presenting M-values growing from o vertex to the next
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int intval;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_int (context, -1);
    else
      {
	  intval = gaiaIsValidTrajectory (geo);
	  sqlite3_result_int (context, intval);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_TrajectoryInterpolatePoint (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ ST_TrajectoryInterpolatePoint(BLOBencoded geometry, DOUBLE m)
/
/ returns a new geometry representing a point interpolated along a Trajectory
/ accordingly to given M-Value
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int intval;
    double m;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  intval = sqlite3_value_int (argv[1]);
	  m = intval;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	m = sqlite3_value_double (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_int (context, -1);
    else
      {
	  intval = gaiaIsValidTrajectory (geo);
	  result = gaiaTrajectoryInterpolatePoint (geo, m);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
	  gaiaFreeGeomColl (geo);
      }
}

#ifndef OMIT_PROJ		/* including PROJ.4 */

static void
fnct_Transform (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Transform(BLOBencoded geometry, srid)
/
/ returns a new geometry that is the original one received, but 
/ transformed / translated to the new SRID [coordinates translation 
/ is applied]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int srid_from;
    int srid_to;
    char *proj_from;
    char *proj_to;
    void *data = sqlite3_user_data (context);
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	srid_to = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  srid_from = geo->Srid;
	  getProjParams (sqlite, srid_from, &proj_from);
	  getProjParams (sqlite, srid_to, &proj_to);
	  if (proj_to == NULL || proj_from == NULL)
	    {
		if (proj_from)
		    free (proj_from);
		if (proj_to)
		    free (proj_to);
		gaiaFreeGeomColl (geo);
		sqlite3_result_null (context);
		return;
	    }
	  if (data != NULL)
	      result = gaiaTransform_r (data, geo, proj_from, proj_to);
	  else
	      result = gaiaTransform (geo, proj_from, proj_to);
	  free (proj_from);
	  free (proj_to);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = srid_to;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_TransformXY (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ TransformXY(BLOBencoded geometry, srid)
/
/ returns a new geometry that is the original one received, but 
/ transformed / translated to the new SRID [coordinates translation 
/ is applied]
/
/ NOTE: this is a special "flavor" of ST_Transform()
/       just X and Y coordinates will be transformed,
/       Z and M values (if eventually present) will be 
/       left untouched.
/       Mainly intended as a workaround possibily useful
/       when facing partially broken PROJ.4 definitions.
/
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int srid_from;
    int srid_to;
    char *proj_from;
    char *proj_to;
    void *data = sqlite3_user_data (context);
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	srid_to = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  srid_from = geo->Srid;
	  getProjParams (sqlite, srid_from, &proj_from);
	  getProjParams (sqlite, srid_to, &proj_to);
	  if (proj_to == NULL || proj_from == NULL)
	    {
		if (proj_from)
		    free (proj_from);
		if (proj_to)
		    free (proj_to);
		gaiaFreeGeomColl (geo);
		sqlite3_result_null (context);
		return;
	    }
	  if (data != NULL)
	      result = gaiaTransformXY_r (data, geo, proj_from, proj_to);
	  else
	      result = gaiaTransformXY (geo, proj_from, proj_to);
	  free (proj_from);
	  free (proj_to);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = srid_to;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

#endif /* end including PROJ.4 */

#ifndef OMIT_GEOS		/* including GEOS */

static void
fnct_GEOS_GetLastWarningMsg (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ GEOS_GetLastWarningMsg()
/
/ return the most recent GEOS warning message (if any)
/ return NULL on any other case
*/
    const char *msg;
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (data != NULL)
	msg = gaiaGetGeosWarningMsg_r (data);
    else
	msg = gaiaGetGeosWarningMsg ();
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_GEOS_GetLastErrorMsg (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ GEOS_GetLastErrorMsg()
/
/ return the most recent GEOS error message (if any)
/ return NULL on any other case
*/
    const char *msg;
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (data != NULL)
	msg = gaiaGetGeosErrorMsg_r (data);
    else
	msg = gaiaGetGeosErrorMsg ();
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_GEOS_GetLastAuxErrorMsg (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ GEOS_GetLastAuxErrorMsg()
/
/ return the most recent GEOS error message (if any)
/ return NULL on any other case
*/
    const char *msg;
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (data != NULL)
	msg = gaiaGetGeosAuxErrorMsg_r (data);
    else
	msg = gaiaGetGeosAuxErrorMsg ();
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_GEOS_GetCriticalPointFromMsg (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ GEOS_GetCriticalPointFromMsg()
/
/ return a Point Geometry by (possibly) parsing the most recent GEOS error/warning message 
/ return NULL on any other case
*/
    int srid = -1;
    gaiaGeomCollPtr geom;
    void *data = sqlite3_user_data (context);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  srid = sqlite3_value_int (argv[0]);
      }
    if (data != NULL)
	geom = gaiaCriticalPointFromGEOSmsg_r (data);
    else
	geom = gaiaCriticalPointFromGEOSmsg ();
    if (geom == NULL)
	sqlite3_result_null (context);
    else
      {
	  unsigned char *blob;
	  int len;
	  geom->Srid = srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &blob, &len, gpkg_mode, tiny_point);
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_blob (context, blob, len, free);
      }
}

static void
fnct_IsValidReason (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsValidReason(geom [ , esri_flag] )
/ ST_IsValidReason(geom [ , esri_flag] )
/
/ return a TEXT string stating if a Geometry is valid
/ and if not valid, a reason why
/ return NULL on any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaGeomCollPtr geom;
    int esri_flag = 0;
    char *str;
    void *data = sqlite3_user_data (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  esri_flag = sqlite3_value_int (argv[1]);
      }
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (esri_flag)
      {
	  gaiaGeomCollPtr detail;
	  if (data != NULL)
	      detail = gaiaIsValidDetailEx_r (data, geom, esri_flag);
	  else
	      detail = gaiaIsValidDetailEx (geom, esri_flag);
	  if (detail == NULL)
	    {
		/* performing extra checks */
		if (data != NULL)
		  {
		      if (gaiaIsToxic_r (data, geom))
			  sqlite3_result_text (context,
					       "Invalid: Toxic Geometry ... too few points",
					       -1, SQLITE_TRANSIENT);
		      else if (gaiaIsNotClosedGeomColl_r (data, geom))
			  sqlite3_result_text (context,
					       "Invalid: Unclosed Rings were detected",
					       -1, SQLITE_TRANSIENT);
		      else
			  sqlite3_result_text (context, "Valid Geometry", -1,
					       SQLITE_TRANSIENT);
		  }
		else
		  {
		      if (gaiaIsToxic (geom))
			  sqlite3_result_text (context,
					       "Invalid: Toxic Geometry ... too few points",
					       -1, SQLITE_TRANSIENT);
		      else if (gaiaIsNotClosedGeomColl (geom))
			  sqlite3_result_text (context,
					       "Invalid: Unclosed Rings were detected",
					       -1, SQLITE_TRANSIENT);
		      else
			  sqlite3_result_text (context, "Valid Geometry", -1,
					       SQLITE_TRANSIENT);
		  }
		goto end;
	    }
	  else
	      gaiaFreeGeomColl (detail);
      }
    if (data != NULL)
	str = gaiaIsValidReason_r (data, geom);
    else
	str = gaiaIsValidReason (geom);
    if (str == NULL)
	sqlite3_result_null (context);
    else
      {
	  len = strlen (str);
	  sqlite3_result_text (context, str, len, free);
      }
  end:
    if (geom != NULL)
	gaiaFreeGeomColl (geom);
}

static void
fnct_IsValidDetail (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsValidDetail(geom [ , esri_flag] )
/ ST_IsValidDetail(geom [ , esri_flag] )
/
/ return a Geometry detail causing a Geometry to be invalid
/ return NULL on any other case
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    gaiaGeomCollPtr geom;
    gaiaGeomCollPtr detail;
    int esri_flag = 0;
    unsigned char *p_result = NULL;
    void *data = sqlite3_user_data (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  esri_flag = sqlite3_value_int (argv[1]);
      }
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (data != NULL)
	detail = gaiaIsValidDetailEx_r (data, geom, esri_flag);
    else
	detail = gaiaIsValidDetailEx (geom, esri_flag);
    if (detail == NULL)
	sqlite3_result_null (context);
    else
      {
	  detail->Srid = geom->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (detail, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    if (geom != NULL)
	gaiaFreeGeomColl (geom);
    if (detail != NULL)
	gaiaFreeGeomColl (detail);
}

static void
fnct_Boundary (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Boundary(BLOB encoded geometry)
/
/ returns the combinatorial boundary for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr boundary;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaIsEmpty (geo))
	      sqlite3_result_null (context);
	  else
	    {
		void *data = sqlite3_user_data (context);
		if (data != NULL)
		    boundary = gaiaBoundary_r (data, geo);
		else
		    boundary = gaiaBoundary (geo);
		if (!boundary)
		    sqlite3_result_null (context);
		else
		  {
		      gaiaToSpatiaLiteBlobWkbEx2 (boundary, &p_result, &len,
						  gpkg_mode, tiny_point);
		      gaiaFreeGeomColl (boundary);
		      sqlite3_result_blob (context, p_result, len, free);
		  }
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsClosed (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsClosed(BLOB encoded LINESTRING or MULTILINESTRING geometry)
/
/ returns:
/ 1 if this LINESTRING is closed [or if this is a MULTILINESTRING and every LINESTRINGs are closed] 
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  sqlite3_result_int (context, gaiaIsClosedGeom (geo));
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsSimple (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsSimple(BLOB encoded GEOMETRY)
/
/ returns:
/ 1 if this GEOMETRY is simple
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaIsSimple_r (data, geo);
	  else
	      ret = gaiaIsSimple (geo);
	  if (ret < 0)
	      sqlite3_result_int (context, -1);
	  else
	      sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsRing (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsRing(BLOB encoded LINESTRING geometry)
/
/ returns:
/ 1 if this LINESTRING is a valid RING
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    gaiaLinestringPtr line;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  line = simpleLinestring (geo);
	  if (line == NULL)
	      sqlite3_result_int (context, -1);
	  else
	    {
		void *data = sqlite3_user_data (context);
		if (data != NULL)
		    ret = gaiaIsRing_r (data, line);
		else
		    ret = gaiaIsRing (line);
		sqlite3_result_int (context, ret);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_IsValid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsValid(BLOB encoded GEOMETRY [ , BOOLEAN esri_flag] )
/
/ returns:
/ 1 if this GEOMETRY is a valid one
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    int esri_flag = 0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  esri_flag = sqlite3_value_int (argv[1]);
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (esri_flag)
	    {
		gaiaGeomCollPtr detail;
		if (data != NULL)
		    detail = gaiaIsValidDetailEx_r (data, geo, esri_flag);
		else
		    detail = gaiaIsValidDetailEx (geo, esri_flag);
		if (detail == NULL)
		  {
		      /* extra checks */
		      int extra = 0;
		      if (data != NULL)
			{
			    if (gaiaIsToxic_r (data, geo))
				extra = 1;
			    if (gaiaIsNotClosedGeomColl_r (data, geo))
				extra = 1;
			}
		      else
			{
			    if (gaiaIsToxic (geo))
				extra = 1;
			    if (gaiaIsNotClosedGeomColl (geo))
				extra = 1;
			}
		      if (extra)
			  sqlite3_result_int (context, 0);
		      else
			  sqlite3_result_int (context, 1);
		  }
		else
		  {
		      gaiaFreeGeomColl (detail);
		      sqlite3_result_int (context, 0);
		  }
		goto end;
	    }
	  if (data != NULL)
	      ret = gaiaIsValid_r (data, geo);
	  else
	      ret = gaiaIsValid (geo);
	  if (ret < 0)
	      sqlite3_result_int (context, -1);
	  else
	      sqlite3_result_int (context, ret);
      }
  end:
    gaiaFreeGeomColl (geo);
}

static void
length_common (const void *p_cache, sqlite3_context * context, int argc,
	       sqlite3_value ** argv, int is_perimeter)
{
/* common implementation supporting both ST_Length and ST_Perimeter */
    unsigned char *p_blob;
    int n_bytes;
    double length = 0.0;
    int ret;
    int use_ellipsoid = -1;
    double a;
    double b;
    double rf;
    gaiaGeomCollPtr geo = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  use_ellipsoid = sqlite3_value_int (argv[1]);
	  if (use_ellipsoid != 0)
	      use_ellipsoid = 1;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (use_ellipsoid >= 0)
	    {
		/* attempting to identify the corresponding ellipsoid */
		if (getEllipsoidParams (sqlite, geo->Srid, &a, &b, &rf))
		  {
		      double l;
		      int ib;
		      gaiaLinestringPtr line;
		      gaiaPolygonPtr polyg;
		      gaiaRingPtr ring;
		      if (use_ellipsoid)
			{
			    /* measuring on the Ellipsoid */
			    if (!is_perimeter)
			      {
				  line = geo->FirstLinestring;
				  while (line)
				    {
					/* Linestrings */
					l = gaiaGeodesicTotalLength (a,
								     b,
								     rf,
								     line->DimensionModel,
								     line->
								     Coords,
								     line->
								     Points);
					if (l < 0.0)
					  {
					      length = -1.0;
					      break;
					  }
					length += l;
					line = line->Next;
				    }
			      }
			    if (length >= 0)
			      {
				  if (is_perimeter)
				    {
					/* Polygons */
					polyg = geo->FirstPolygon;
					while (polyg)
					  {
					      /* exterior Ring */
					      ring = polyg->Exterior;
					      l = gaiaGeodesicTotalLength (a,
									   b,
									   rf,
									   ring->
									   DimensionModel,
									   ring->
									   Coords,
									   ring->
									   Points);
					      if (l < 0.0)
						{
						    length = -1.0;
						    break;
						}
					      length += l;
					      for (ib = 0;
						   ib <
						   polyg->NumInteriors; ib++)
						{
						    /* interior Rings */
						    ring =
							polyg->Interiors + ib;
						    l = gaiaGeodesicTotalLength
							(a, b, rf,
							 ring->DimensionModel,
							 ring->Coords,
							 ring->Points);
						    if (l < 0.0)
						      {
							  length = -1.0;
							  break;
						      }
						    length += l;
						}
					      if (length < 0.0)
						  break;
					      polyg = polyg->Next;
					  }
				    }
			      }
			}
		      else
			{
			    /* measuring on the Great Circle */
			    if (!is_perimeter)
			      {
				  line = geo->FirstLinestring;
				  while (line)
				    {
					/* Linestrings */
					length +=
					    gaiaGreatCircleTotalLength
					    (a, b, line->DimensionModel,
					     line->Coords, line->Points);
					line = line->Next;
				    }
			      }
			    if (length >= 0)
			      {
				  if (is_perimeter)
				    {
					/* Polygons */
					polyg = geo->FirstPolygon;
					while (polyg)
					  {
					      /* exterior Ring */
					      ring = polyg->Exterior;
					      length +=
						  gaiaGreatCircleTotalLength
						  (a, b,
						   ring->DimensionModel,
						   ring->Coords, ring->Points);
					      for (ib = 0;
						   ib < polyg->NumInteriors;
						   ib++)
						{
						    /* interior Rings */
						    ring =
							polyg->Interiors + ib;
						    length +=
							gaiaGreatCircleTotalLength
							(a, b,
							 ring->DimensionModel,
							 ring->Coords,
							 ring->Points);
						}
					      polyg = polyg->Next;
					  }
				    }
			      }
			}
		      if (length < 0.0)
			{
			    /* invalid distance */
			    sqlite3_result_null (context);
			}
		      else
			  sqlite3_result_double (context, length);
		  }
		else
		    sqlite3_result_null (context);
		goto stop;
	    }
	  else if (p_cache != NULL)
	      ret =
		  gaiaGeomCollLengthOrPerimeter_r (p_cache, geo, is_perimeter,
						   &length);
	  else
	      ret = gaiaGeomCollLengthOrPerimeter (geo, is_perimeter, &length);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, length);
      }
  stop:
    gaiaFreeGeomColl (geo);
}

static void
fnct_Length (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Length(BLOB encoded GEOMETRYCOLLECTION)
/ ST_Length(BLOB encoded GEOMETRYCOLLECTION, Boolean use_ellipsoid)
/
/ returns  the total length for current geometry 
/ or NULL if any error is encountered
/
/ Please note: starting since 4.0.0 this function will ignore
/ any Polygon (only Linestrings will be considered)
/
*/
    void *data = sqlite3_user_data (context);
    length_common (data, context, argc, argv, 0);
}

static void
fnct_Perimeter (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Perimeter(BLOB encoded GEOMETRYCOLLECTION)
/ ST_Perimeter(BLOB encoded GEOMETRYCOLLECTION, Boolean use_ellipsoid)
/
/ returns  the total perimeter length for current geometry 
/ or NULL if any error is encountered
/
/ Please note: starting since 4.0.0 this function will ignore
/ any Linestring (only Polygons will be considered)
/
*/
    void *data = sqlite3_user_data (context);
    length_common (data, context, argc, argv, 1);
}

#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */

static void
fnct_3dLength (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_3dLength(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns  the total 2D or 3D length for current geometry
/ accordingly to the Geometry dimensions 
/ returns NULL if any error is encountered
/
/ Please note: this function will ignore
/ any Polygon (only Linestrings will be considered)
/
*/
    unsigned char *p_blob;
    int n_bytes;
    double length = 0.0;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  ret = gaia3dLength (cache, geo, &length);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, length);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_FromTWKB (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromTWKB(TWKB encoded geometry)
/   or
/ GeomFromTWKB(TWKB encoded geometry, INT srid)
/
/ returns the current geometry by parsing a TWKB encoded string 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *twkb;
    int twkb_size;
    int srid = -1;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  twkb = sqlite3_value_blob (argv[0]);
	  twkb_size = sqlite3_value_bytes (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  srid = sqlite3_value_int (argv[1]);
	  if (srid < 0)
	      srid = -1;
      }
    geo = gaiaFromTWKB (cache, twkb, twkb_size, srid);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &len, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_ToTWKB (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsTWKB(BLOB encoded geometry)
/   or
/ AsTWKB(BLOB encoded geometry, INT precision_xy)
/   or
/ AsTWKB(BLOB encoded geometry, INT precision_xy, INT precision_z)
/   or
/ AsTWKB(BLOB encoded geometry, INT precision_xy, INT precision_z, 
/        INT precision_m)
/   or
/ AsTWKB(BLOB encoded geometry, INT precision_xy, INT precision_z, 
/        INT precision_m, INT with_size)
/   or
/ AsTWKB(BLOB encoded geometry, INT precision_xy, INT precision_z, 
/        INT precision_m, INT with_size, INT with_bbox)
/
/ returns a text string corresponding to compressed TWKB notation 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int value;
    unsigned char precision_xy = 0;
    unsigned char precision_z = 0;
    unsigned char precision_m = 0;
    int with_size = 0;
    int with_bbox = 0;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = sqlite3_value_int (argv[1]);
	  if (value < 0)
	      precision_xy = 0;
	  else if (value > 20)
	      precision_xy = 20;
	  else
	      precision_xy = value;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = sqlite3_value_int (argv[2]);
	  if (value < 0)
	      precision_z = 0;
	  else if (value > 20)
	      precision_z = 20;
	  else
	      precision_z = value;
      }
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = sqlite3_value_int (argv[3]);
	  if (value < 0)
	      precision_m = 0;
	  else if (value > 20)
	      precision_m = 20;
	  else
	      precision_m = value;
      }
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  with_size = sqlite3_value_int (argv[4]);
	  if (with_size != 0)
	      with_size = 1;
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  with_bbox = sqlite3_value_int (argv[5]);
	  if (with_bbox != 0)
	      with_bbox = 1;
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  unsigned char *twkb;
	  int size_twkb;
	  ret =
	      gaiaToTWKB (cache, geo, precision_xy, precision_z, precision_m,
			  with_size, with_bbox, &twkb, &size_twkb);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, twkb, size_twkb, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_AsEncodedPolyline (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ST_AsEncodedPolyline(BLOB encoded geometry)
/   or
/ ST_AsEncodedPolyline(BLOB encoded geometry, INT precision)
/
/ returns a text string corresponding to GoogleMaps encoded Polyline
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int value;
    unsigned char precision = 5;
    int ret;
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = sqlite3_value_int (argv[1]);
	  if (value < 0)
	      precision = 0;
	  else if (value > 20)
	      precision = 20;
	  else
	      precision = value;
      }
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  int invalid = 0;
	  int geographic = 0;
	  char *encoded;
	  int size_encoded;
	  if (geo->FirstPoint == NULL && geo->FirstPolygon == NULL
	      && geo->FirstLinestring != NULL
	      && geo->FirstLinestring == geo->LastLinestring)
	      ;
	  else
	      invalid = 1;
	  if (!srid_is_geographic (sqlite, geo->Srid, &geographic))
	      invalid = 1;
	  if (!geographic)
	      invalid = 1;
	  if (invalid)
	    {
		gaiaFreeGeomColl (geo);
		sqlite3_result_null (context);
		return;
	    }
	  ret =
	      gaiaAsEncodedPolyLine (cache, geo, precision, &encoded,
				     &size_encoded);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_text (context, encoded, size_encoded, free);
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_LineFromEncodedPolyline (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ ST_LineFromEncodedPolyline(TEXT encoded geometry)
/   or
/ ST_LineFromEncodedPolyline(TEXT encoded geometry, INT precision)
/
/ returns a Linestring Geometry from a text string corresponding to
/ GoogleMaps encoded Polyline
/ or NULL if any error is encountered
*/
    unsigned char *p_result = NULL;
    int size;
    const char *encoded;
    gaiaGeomCollPtr geo = NULL;
    int value;
    unsigned char precision = 5;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	gpkg_mode = cache->gpkg_mode;
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
	encoded = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = sqlite3_value_int (argv[1]);
	  if (value < 0)
	      precision = 0;
	  else if (value > 20)
	      precision = 20;
	  else
	      precision = value;
      }
    geo = gaiaLineFromEncodedPolyline (cache, encoded, precision);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geo, &p_result, &size, gpkg_mode, 0);
    gaiaFreeGeomColl (geo);
    sqlite3_result_blob (context, p_result, size, free);
}

#endif /* end RTTOPO conditional */

static void
fnct_Area (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Area(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns the total area for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double area = 0.0;
    int ret;
    int use_ellipsoid = -1;
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
    double a;
    double b;
    double rf;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
#endif /* end RTTOPO conditional */
    gaiaGeomCollPtr geo = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  use_ellipsoid = sqlite3_value_int (argv[1]);
	  if (use_ellipsoid != 0)
	      use_ellipsoid = 1;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (use_ellipsoid >= 0)
	    {
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
		/* attempting to identify the corresponding ellipsoid */
		if (getEllipsoidParams (sqlite, geo->Srid, &a, &b, &rf))
		    ret =
			gaiaGeodesicArea (cache, geo, a, b, use_ellipsoid,
					  &area);
		else
		    ret = 0;
#else
		ret = 0;
#endif /* end RTTOPO conditional */
	    }
	  else
	    {
		void *data = sqlite3_user_data (context);
		if (data != NULL)
		    ret = gaiaGeomCollArea_r (data, geo, &area);
		else
		    ret = gaiaGeomCollArea (geo, &area);
	    }
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, area);
      }
    gaiaFreeGeomColl (geo);
}

static gaiaGeomCollPtr
circularity_polygon (int srid, int dims, gaiaPolygonPtr pg)
{
/* building an individual Polygon for Circularity */
    gaiaGeomCollPtr geom = NULL;
    gaiaPolygonPtr pg2;
    gaiaRingPtr i_rng;
    gaiaRingPtr o_rng;
    if (dims == GAIA_XY_Z)
	geom = gaiaAllocGeomCollXYZ ();
    else if (dims == GAIA_XY_M)
	geom = gaiaAllocGeomCollXYM ();
    else if (dims == GAIA_XY_Z_M)
	geom = gaiaAllocGeomCollXYZM ();
    else
	geom = gaiaAllocGeomColl ();
    geom->Srid = srid;
    i_rng = pg->Exterior;
    pg2 = gaiaAddPolygonToGeomColl (geom, i_rng->Points, 0);
    o_rng = pg2->Exterior;
    /* copying points (only EXTERIOR RING) */
    gaiaCopyRingCoords (o_rng, i_rng);
    return geom;
}

static void
fnct_Circularity (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Circularity(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns the Circularity Index for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double pi = 3.14159265358979323846;
    double area = 0.0;
    double perimeter = 0.0;
    double sum_area = 0.0;
    double sum_perimeter = 0.0;
    int nlns = 0;
    int npgs = 0;
    int ret;
    int use_ellipsoid = -1;
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
    double a;
    double b;
    double rf;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
#endif /* end RTTOPO conditional */
    gaiaGeomCollPtr geo = NULL;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  use_ellipsoid = sqlite3_value_int (argv[1]);
	  if (use_ellipsoid != 0)
	      use_ellipsoid = 1;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (use_ellipsoid >= 0)
	    {
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
		/* attempting to identify the corresponding ellipsoid */
		if (getEllipsoidParams (sqlite, geo->Srid, &a, &b, &rf))
		    ret = 1;
		else
		    ret = 0;
#else
		ret = 0;
#endif /* end RTTOPO conditional */
		if (!ret)
		  {
		      sqlite3_result_null (context);
		      goto end;
		  }
	    }
	  ln = geo->FirstLinestring;
	  while (ln != NULL)
	    {
		nlns++;
		ln = ln->Next;
	    }

	  pg = geo->FirstPolygon;
	  while (pg != NULL)
	    {
		/* looping on individual polygons */
		gaiaGeomCollPtr geo2 =
		    circularity_polygon (geo->Srid, geo->DimensionModel, pg);
		if (use_ellipsoid >= 0)
		  {
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
		      /* attempting to identify the corresponding ellipsoid */
		      ret =
			  gaiaGeodesicArea (cache, geo2, a, b, use_ellipsoid,
					    &area);
#else
		      ret = 0;
#endif /* end RTTOPO conditional */
		  }
		else
		  {
		      if (data != NULL)
			  ret = gaiaGeomCollArea_r (data, geo2, &area);
		      else
			  ret = gaiaGeomCollArea (geo2, &area);
		  }
		if (ret)
		  {
		      sum_area += area;
		      npgs++;
		  }
		else
		  {
		      gaiaFreeGeomColl (geo2);
		      npgs = 0;
		      break;
		  }

		if (use_ellipsoid >= 0)
		  {
#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
		      perimeter = gaiaGeodesicTotalLength (a, b, rf,
							   pg->Exterior->
							   DimensionModel,
							   pg->Exterior->Coords,
							   pg->Exterior->
							   Points);
		      if (perimeter < 0.0)
			  ret = 0;
		      else
			  ret = 1;
#else
		      ret = 0;
#endif /* end RTTOPO conditional */
		  }
		else
		  {
		      if (data != NULL)
			  ret =
			      gaiaGeomCollLengthOrPerimeter_r (data, geo2, 1,
							       &perimeter);
		      else
			  ret =
			      gaiaGeomCollLengthOrPerimeter (geo2, 1,
							     &perimeter);
		  }
		if (ret)
		    sum_perimeter += perimeter;
		else
		  {
		      gaiaFreeGeomColl (geo2);
		      npgs = 0;
		      break;
		  }
		gaiaFreeGeomColl (geo2);
		pg = pg->Next;
	    }
	  if (!npgs)
	    {
		if (nlns)
		    sqlite3_result_double (context, 0.0);
		else
		    sqlite3_result_null (context);
	    }
	  else
	    {
		double index =
		    (4.0 * pi * sum_area) / (sum_perimeter * sum_perimeter);
		sqlite3_result_double (context, index);
	    }
      }
  end:
    gaiaFreeGeomColl (geo);
}

static void
fnct_Centroid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Centroid(BLOBencoded POLYGON or MULTIPOLYGON geometry)
/
/ returns a POINT representing the centroid for current POLYGON / MULTIPOLYGON geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    int ret;
    double x;
    double y;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaIsEmpty (geo))
	      sqlite3_result_null (context);
	  else
	    {
		void *data = sqlite3_user_data (context);
		if (data != NULL)
		    ret = gaiaGeomCollCentroid_r (data, geo, &x, &y);
		else
		    ret = gaiaGeomCollCentroid (geo, &x, &y);
		if (!ret)
		    sqlite3_result_null (context);
		else
		  {
		      result = gaiaAllocGeomColl ();
		      result->Srid = geo->Srid;
		      gaiaAddPointToGeomColl (result, x, y);
		      gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
						  gpkg_mode, tiny_point);
		      gaiaFreeGeomColl (result);
		      sqlite3_result_blob (context, p_result, len, free);
		  }
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_PointOnSurface (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ PointOnSurface(BLOBencoded POLYGON or MULTIPOLYGON geometry)
/
/ returns a POINT guaranteed to lie on the Surface
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    double x;
    double y;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  int posret;
	  if (data != NULL)
	      posret = gaiaGetPointOnSurface_r (data, geo, &x, &y);
	  else
	      posret = gaiaGetPointOnSurface (geo, &x, &y);
	  if (!posret)
	      sqlite3_result_null (context);
	  else
	    {
		if (geo->DimensionModel == GAIA_XY_Z)
		  {
		      result = gaiaAllocGeomCollXYZ ();
		      gaiaAddPointToGeomCollXYZ (result, x, y, 0.0);
		  }
		else if (geo->DimensionModel == GAIA_XY_M)
		  {
		      result = gaiaAllocGeomCollXYM ();
		      gaiaAddPointToGeomCollXYM (result, x, y, 0.0);
		  }
		else if (geo->DimensionModel == GAIA_XY_Z_M)
		  {
		      result = gaiaAllocGeomCollXYZM ();
		      gaiaAddPointToGeomCollXYZM (result, x, y, 0.0, 0.0);
		  }
		else
		  {
		      result = gaiaAllocGeomColl ();
		      gaiaAddPointToGeomColl (result, x, y);
		  }
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		gaiaFreeGeomColl (result);
		sqlite3_result_blob (context, p_result, len, free);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Simplify (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Simplify(BLOBencoded geometry, tolerance)
/
/ returns a new geometry that is a caricature of the original one received, but simplified using the Douglas-Peuker algorihtm
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int int_value;
    double tolerance;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	tolerance = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  tolerance = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaGeomCollSimplify_r (data, geo, tolerance);
	  else
	      result = gaiaGeomCollSimplify (geo, tolerance);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SimplifyPreserveTopology (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ SimplifyPreserveTopology(BLOBencoded geometry, tolerance)
/
/ returns a new geometry that is a caricature of the original one received, but simplified using the Douglas-Peuker algorihtm [preserving topology]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int int_value;
    double tolerance;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	tolerance = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  tolerance = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaGeomCollSimplifyPreserveTopology_r (data, geo, tolerance);
	  else
	      result = gaiaGeomCollSimplifyPreserveTopology (geo, tolerance);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ConvexHull (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ConvexHull(BLOBencoded geometry)
/
/ returns a new geometry representing the CONVEX HULL for current geometry
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int len;
    unsigned char *p_result = NULL;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaConvexHull_r (data, geo);
	  else
	      result = gaiaConvexHull (geo);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Buffer (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Buffer(BLOBencoded geometry, radius)
/ Buffer(BLOBencoded geometry, radius, quadrantsegments)
/
/ returns a new geometry representing the BUFFER for current geometry
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    double radius;
    int int_value;
    int quadrantsegments = 30;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	radius = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  radius = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  quadrantsegments = sqlite3_value_int (argv[2]);
	  if (quadrantsegments <= 0)
	      quadrantsegments = 1;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaGeomCollBuffer_r (data, geo, radius, quadrantsegments);
	  else
	      result = gaiaGeomCollBuffer (geo, radius, quadrantsegments);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Intersection (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Intersection(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns a new geometry representing the INTERSECTION of both geometries
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaGeometryIntersection_r (data, geo1, geo2);
	  else
	      result = gaiaGeometryIntersection (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else if (gaiaIsEmpty (result))
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static int
gaia_union_polygs (gaiaGeomCollPtr geom)
{
/* testing if this geometry simply contains Polygons */
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts || lns)
	return 0;
    if (!pgs)
	return 0;
    return 1;
}

static void
fnct_Union_step (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Union(BLOBencoded geom)
/
/ aggregate function - STEP
/
*/
    struct gaia_geom_chain *chain;
    struct gaia_geom_chain_item *item;
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    struct gaia_geom_chain **p;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geom)
	return;
    p = sqlite3_aggregate_context (context, sizeof (struct gaia_geom_chain **));
    if (!(*p))
      {
	  /* this is the first row */
	  chain = malloc (sizeof (struct gaia_geom_chain));
	  *p = chain;
	  item = malloc (sizeof (struct gaia_geom_chain_item));
	  item->geom = geom;
	  item->next = NULL;
	  chain->all_polygs = gaia_union_polygs (geom);
	  chain->first = item;
	  chain->last = item;
      }
    else
      {
	  /* subsequent rows */
	  chain = *p;
	  item = malloc (sizeof (struct gaia_geom_chain_item));
	  item->geom = geom;
	  item->next = NULL;
	  if (!gaia_union_polygs (geom))
	      chain->all_polygs = 0;
	  chain->last->next = item;
	  chain->last = item;
      }
}

static void
gaia_free_geom_chain (struct gaia_geom_chain *chain)
{
    struct gaia_geom_chain_item *p = chain->first;
    struct gaia_geom_chain_item *pn;
    while (p)
      {
	  pn = p->next;
	  gaiaFreeGeomColl (p->geom);
	  free (p);
	  p = pn;
      }
    free (chain);
}

static void
fnct_Union_final (sqlite3_context * context)
{
/* SQL function:
/ Union(BLOBencoded geom)
/
/ aggregate function - FINAL
/
*/
    gaiaGeomCollPtr tmp;
    struct gaia_geom_chain *chain;
    struct gaia_geom_chain_item *item;
    gaiaGeomCollPtr aggregate = NULL;
    gaiaGeomCollPtr result;
    void *data = sqlite3_user_data (context);
    struct gaia_geom_chain **p = sqlite3_aggregate_context (context, 0);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    chain = *p;

/* applying UnaryUnion */
    item = chain->first;
    while (item)
      {
	  gaiaGeomCollPtr geom = item->geom;
	  if (item == chain->first)
	    {
		/* initializing the aggregate geometry */
		aggregate = geom;
		item->geom = NULL;
		item = item->next;
		continue;
	    }
	  if (data != NULL)
	      tmp = gaiaMergeGeometries_r (data, aggregate, geom);
	  else
	      tmp = gaiaMergeGeometries (aggregate, geom);
	  gaiaFreeGeomColl (geom);
	  item->geom = NULL;
	  aggregate = tmp;
	  item = item->next;
      }
    if (data != NULL)
	result = gaiaUnaryUnion_r (data, aggregate);
    else
	result = gaiaUnaryUnion (aggregate);
    gaiaFreeGeomColl (aggregate);
    gaia_free_geom_chain (chain);

    if (result == NULL)
	sqlite3_result_null (context);
    else if (gaiaIsEmpty (result))
	sqlite3_result_null (context);
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
      }
    gaiaFreeGeomColl (result);
}

static void
fnct_Union (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Union(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns a new geometry representing the UNION of both geometries
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaGeometryUnion_r (data, geo1, geo2);
	  else
	      result = gaiaGeometryUnion (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else if (gaiaIsEmpty (result))
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Difference (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Difference(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns a new geometry representing the DIFFERENCE of both geometries
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaGeometryDifference_r (data, geo1, geo2);
	  else
	      result = gaiaGeometryDifference (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else if (gaiaIsEmpty (result))
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_SymDifference (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SymDifference(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns a new geometry representing the SYMMETRIC DIFFERENCE of both geometries
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaGeometrySymDifference_r (data, geo1, geo2);
	  else
	      result = gaiaGeometrySymDifference (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else if (gaiaIsEmpty (result))
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Equals (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Equals(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries are "spatially equal"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollEquals_r (data, geo1, geo2);
	  else
	      ret = gaiaGeomCollEquals (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Intersects (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Intersects(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries do "spatially intersects"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedIntersects (data,
						    geo1, blob1, bytes1, geo2,
						    blob2, bytes2);
	  else
	      ret = gaiaGeomCollIntersects (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Disjoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Disjoint(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries are "spatially disjoint"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedDisjoint (data,
						  geo1, blob1, bytes1, geo2,
						  blob2, bytes2);
	  else
	      ret = gaiaGeomCollDisjoint (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Overlaps (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Overlaps(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries do "spatially overlaps"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedOverlaps (data,
						  geo1, blob1, bytes1, geo2,
						  blob2, bytes2);
	  else
	      ret = gaiaGeomCollOverlaps (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Crosses (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Crosses(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries do "spatially crosses"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedCrosses (data,
						 geo1, blob1, bytes1, geo2,
						 blob2, bytes2);
	  else
	      ret = gaiaGeomCollCrosses (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Touches (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Touches(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if the two geometries do "spatially touches"
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedTouches (data,
						 geo1, blob1, bytes1, geo2,
						 blob2, bytes2);
	  else
	      ret = gaiaGeomCollTouches (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Within (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Within(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if GEOM-1 is completely contained within GEOM-2
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedWithin (data, geo1,
						blob1, bytes1, geo2, blob2,
						bytes2);
	  else
	      ret = gaiaGeomCollWithin (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Contains (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Contains(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if GEOM-1 completely contains GEOM-2
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedContains (data,
						  geo1, blob1, bytes1, geo2,
						  blob2, bytes2);
	  else
	      ret = gaiaGeomCollContains (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Relate (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Relate(BLOBencoded geom1, BLOBencoded geom2, string pattern)
/
/ returns:
/ 1 if GEOM-1 and GEOM-2 have a spatial relationship as specified by the patternMatrix 
/ 0 otherwise
/ or -1 if any error is encountered
/
/ or alternatively:
/
/ Relate(BLOBencoded geom1, BLOBencoded geom2)
/ Relate(BLOBencoded geom1, BLOBencoded geom2, int bnr)
/
/ returns:
/ an intersection matrix [DE-9IM]
/ NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    const char *pattern = NULL;
    int bnr = 1;
    char *matrix;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  if (argc < 3)
	      sqlite3_result_null (context);
	  else
	    {
		if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
		    sqlite3_result_null (context);
		else
		    sqlite3_result_int (context, -1);
	    }
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  if (argc < 3)
	      sqlite3_result_null (context);
	  else
	    {
		if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
		    sqlite3_result_null (context);
		else
		    sqlite3_result_int (context, -1);
	    }
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      pattern = (const char *) sqlite3_value_text (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      bnr = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
      {
	  if (pattern == NULL)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_int (context, -1);
      }
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (pattern != NULL)
	    {
		/* evaluating the given intersection matrix pattern */
		if (data != NULL)
		    ret = gaiaGeomCollRelate_r (data, geo1, geo2, pattern);
		else
		    ret = gaiaGeomCollRelate (geo1, geo2, pattern);
		sqlite3_result_int (context, ret);
	    }
	  else
	    {
		/* returning an intersection matrix */
		if (data != NULL)
		    matrix =
			gaiaGeomCollRelateBoundaryNodeRule_r (data, geo1,
							      geo2, bnr);
		else
		    matrix =
			gaiaGeomCollRelateBoundaryNodeRule (geo1, geo2, bnr);
		if (matrix == NULL)
		    sqlite3_result_null (context);
		else
		    sqlite3_result_text (context, matrix, strlen (matrix),
					 free);
	    }
      }
    if (geo1 != NULL)
	gaiaFreeGeomColl (geo1);
    if (geo2 != NULL)
	gaiaFreeGeomColl (geo2);
}

static void
fnct_RelateMatch (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_RelateMatch(string matrix, string pattern)
/
/ returns:
/ 1 if the intersection matrix satisfies the intersection pattern
/ 0 otherwise
/ or -1 if any error is encountered
*/
    int ret;
    const char *matrix;
    const char *pattern;
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    else
	matrix = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    else
	pattern = (char *) sqlite3_value_text (argv[1]);
    if (data != NULL)
	ret = gaiaIntersectionMatrixPatternMatch_r (data, matrix, pattern);
    else
	ret = gaiaIntersectionMatrixPatternMatch (matrix, pattern);
    sqlite3_result_int (context, ret);
}

static void
fnct_Distance (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Distance(BLOBencoded geom1, BLOBencoded geom2)
/ Distance(BLOBencoded geom1, BLOBencoded geom2, Boolen use_ellipsoid)
/
/ returns the distance between GEOM-1 and GEOM-2
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    double dist;
    int use_ellipsoid = -1;
    double a;
    double b;
    double rf;
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    void *data = sqlite3_user_data (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  use_ellipsoid = sqlite3_value_int (argv[2]);
	  if (use_ellipsoid != 0)
	      use_ellipsoid = 1;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  if (use_ellipsoid >= 0)
	    {
		/* checking first if an intersection exists */
		if (data != NULL)
		    ret = gaiaGeomCollIntersects_r (data, geo1, geo2);
		else
		    ret = gaiaGeomCollIntersects (geo1, geo2);
		if (ret)
		  {
		      /* if an intersection exists the distance is always ZERO */
		      sqlite3_result_double (context, 0.0);
		      goto stop;
		  }

		/* attempting to identify the corresponding ellipsoid */
		if (getEllipsoidParams (sqlite, geo1->Srid, &a, &b, &rf))
		  {
		      gaiaGeomCollPtr shortest;
		      if (data != NULL)
			  shortest = gaiaShortestLine_r (data, geo1, geo2);
		      else
			  shortest = gaiaShortestLine (geo1, geo2);
		      if (shortest == NULL)
			  sqlite3_result_null (context);
		      else if (shortest->FirstLinestring == NULL)
			{
			    gaiaFreeGeomColl (shortest);
			    sqlite3_result_null (context);
			}
		      else
			{
			    /* computes the metric distance */
			    double x0;
			    double y0;
			    double x1;
			    double y1;
			    double z;
			    double m;
			    gaiaLinestringPtr ln = shortest->FirstLinestring;
			    dist = -1.0;
			    if (ln->Points == 2)
			      {
				  if (ln->DimensionModel == GAIA_XY_Z)
				    {
					gaiaGetPointXYZ (ln->Coords, 0,
							 &x0, &y0, &z);
				    }
				  else if (ln->DimensionModel == GAIA_XY_M)
				    {
					gaiaGetPointXYM (ln->Coords, 0,
							 &x0, &y0, &m);
				    }
				  else if (ln->DimensionModel == GAIA_XY_Z_M)
				    {
					gaiaGetPointXYZM (ln->Coords, 0,
							  &x0, &y0, &z, &m);
				    }
				  else
				    {
					gaiaGetPoint (ln->Coords, 0, &x0, &y0);
				    }
				  if (ln->DimensionModel == GAIA_XY_Z)
				    {
					gaiaGetPointXYZ (ln->Coords, 1,
							 &x1, &y1, &z);
				    }
				  else if (ln->DimensionModel == GAIA_XY_M)
				    {
					gaiaGetPointXYM (ln->Coords, 1,
							 &x1, &y1, &m);
				    }
				  else if (ln->DimensionModel == GAIA_XY_Z_M)
				    {
					gaiaGetPointXYZM (ln->Coords, 1,
							  &x1, &y1, &z, &m);
				    }
				  else
				    {
					gaiaGetPoint (ln->Coords, 1, &x1, &y1);
				    }
				  if (use_ellipsoid)
				      dist =
					  gaiaGeodesicDistance (a, b,
								rf, y0,
								x0, y1, x1);
				  else
				    {
					a = 6378137.0;
					rf = 298.257223563;
					b = (a * (1.0 - (1.0 / rf)));
					dist =
					    gaiaGreatCircleDistance (a,
								     b,
								     y0,
								     x0,
								     y1, x1);
				    }
				  if (dist < 0.0)
				    {
					/* invalid distance */
					sqlite3_result_null (context);
				    }
				  else
				      sqlite3_result_double (context, dist);
			      }
			    else
				sqlite3_result_null (context);
			    gaiaFreeGeomColl (shortest);
			}
		  }
		else
		    sqlite3_result_null (context);
		goto stop;
	    }
	  else
	    {
		if (data != NULL)
		    ret = gaiaGeomCollDistance_r (data, geo1, geo2, &dist);
		else
		    ret = gaiaGeomCollDistance (geo1, geo2, &dist);
		if (!ret)
		    sqlite3_result_null (context);
		else
		    sqlite3_result_double (context, dist);
	    }
      }
  stop:
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_PtDistWithin (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ PtDistWithin(BLOBencoded geom1, BLOBencoded geom2, double dist 
/ [, boolen use_spheroid])
/
/ returns TRUE if the distance between GEOM-1 and GEOM-2
/ is less or equal to dist
/
/ - if both geom1 and geom2 are in the 4326 (WGS84) SRID,
/   (and does actually contains a single POINT each one)
/   dist is assumed to be measured in Meters
/ - in this case the optional arg use_spheroid is
/   checked to determine if geodesic distance has to be
/   computed on the sphere (quickest) or on the spheroid 
/   default: use_spheroid = FALSE
/ 
/ in any other case the "plain" distance is evaluated
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    double ref_dist;
    int use_spheroid = 0;
    double x0 = 0.0;
    double y0 = 0.0;
    double x1 = 0.0;
    double y1 = 0.0;
    int pt0 = 0;
    int ln0 = 0;
    int pg0 = 0;
    int pt1 = 0;
    int ln1 = 0;
    int pg1 = 0;
    double dist;
    double a;
    double b;
    double rf;
    int ret;
    void *data = sqlite3_user_data (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER
	|| sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 4)
      {
	  /* optional use_spheroid arg */
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int dst = sqlite3_value_int (argv[2]);
	  ref_dist = dst;
      }
    else
	ref_dist = sqlite3_value_double (argv[2]);
    if (argc == 4)
	use_spheroid = sqlite3_value_int (argv[3]);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  if (geo1->Srid == 4326 && geo2->Srid == 4326)
	    {
		/* checking for single points */
		pt = geo1->FirstPoint;
		while (pt)
		  {
		      x0 = pt->X;
		      y0 = pt->Y;
		      pt0++;
		      pt = pt->Next;
		  }
		ln = geo1->FirstLinestring;
		while (ln)
		  {
		      ln0++;
		      ln = ln->Next;
		  }
		pg = geo1->FirstPolygon;
		while (pg)
		  {
		      pg0++;
		      pg = pg->Next;
		  }
		pt = geo2->FirstPoint;
		while (pt)
		  {
		      x1 = pt->X;
		      y1 = pt->Y;
		      pt1++;
		      pt = pt->Next;
		  }
		ln = geo2->FirstLinestring;
		while (ln)
		  {
		      ln1++;
		      ln = ln->Next;
		  }
		pg = geo2->FirstPolygon;
		while (pg)
		  {
		      pg1++;
		      pg = pg->Next;
		  }
		if (pt0 == 1 && pt1 == 1 && ln0 == 0 && ln1 == 0 && pg0 == 0
		    && pg1 == 0)
		  {
		      /* using geodesic distance */
		      a = 6378137.0;
		      rf = 298.257223563;
		      b = (a * (1.0 - (1.0 / rf)));
		      if (use_spheroid)
			{
			    dist =
				gaiaGeodesicDistance (a, b, rf, y0, x0, y1, x1);
			    if (dist <= ref_dist)
				sqlite3_result_int (context, 1);
			    else
				sqlite3_result_int (context, 0);
			}
		      else
			{
			    dist =
				gaiaGreatCircleDistance (a, b, y0, x0, y1, x1);
			    if (dist <= ref_dist)
				sqlite3_result_int (context, 1);
			    else
				sqlite3_result_int (context, 0);
			}
		      goto stop;
		  }
	    }
/* defaulting to flat distance */
	  if (data != NULL)
	      ret = gaiaGeomCollDistance_r (data, geo1, geo2, &dist);
	  else
	      ret = gaiaGeomCollDistance (geo1, geo2, &dist);
	  if (!ret)
	      sqlite3_result_null (context);
	  if (dist <= ref_dist)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
      }
  stop:
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

SPATIALITE_PRIVATE void
geos_error (const char *fmt, ...)
{
/* reporting some GEOS error */
    va_list ap;
    char *msg;
    va_start (ap, fmt);
    msg = sqlite3_vmprintf (fmt, ap);
    va_end (ap);
    if (msg)
      {
	  spatialite_e ("GEOS error: %s\n", msg);
	  gaiaSetGeosErrorMsg (msg);
	  sqlite3_free (msg);
      }
    else
	gaiaSetGeosErrorMsg (NULL);
}

SPATIALITE_PRIVATE void
geos_warning (const char *fmt, ...)
{
/* reporting some GEOS warning */
    va_list ap;
    char *msg;
    va_start (ap, fmt);
    msg = sqlite3_vmprintf (fmt, ap);
    va_end (ap);
    if (msg)
      {
	  spatialite_e ("GEOS warning: %s\n", msg);
	  gaiaSetGeosWarningMsg (msg);
	  sqlite3_free (msg);
      }
    else
	gaiaSetGeosWarningMsg (NULL);
}

static void
fnct_aux_polygonize (sqlite3_context * context, gaiaGeomCollPtr geom_org,
		     int force_multipolygon, int allow_multipolygon)
{
/* a  common function performing any kind of polygonization op */
    gaiaGeomCollPtr geom_new = NULL;
    int len;
    unsigned char *p_result = NULL;
    gaiaPolygonPtr pg;
    int pgs = 0;
    void *data = sqlite3_user_data (context);
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (!geom_org)
	goto invalid;
    if (data != NULL)
	geom_new = gaiaPolygonize_r (data, geom_org, force_multipolygon);
    else
	geom_new = gaiaPolygonize (geom_org, force_multipolygon);
    if (!geom_new)
	goto invalid;
    gaiaFreeGeomColl (geom_org);
    pg = geom_new->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pgs > 1 && allow_multipolygon == 0)
      {
	  /* invalid: a POLYGON is expected !!! */
	  gaiaFreeGeomColl (geom_new);
	  sqlite3_result_null (context);
	  return;
      }
    gaiaToSpatiaLiteBlobWkbEx2 (geom_new, &p_result, &len, gpkg_mode,
				tiny_point);
    gaiaFreeGeomColl (geom_new);
    sqlite3_result_blob (context, p_result, len, free);
    return;
  invalid:
    if (geom_org)
	gaiaFreeGeomColl (geom_org);
    sqlite3_result_null (context);
}

/*
/ the following functions performs initial argument checking, 
/ and then readdressing the request to fnct_aux_polygonize()
/ for actual processing
*/

static void
fnct_BdPolyFromText1 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BdPolyFromText(WKT encoded MULTILINESTRING)
/
/ returns the current geometry [POLYGON] by parsing a WKT encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, -1);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    fnct_aux_polygonize (context, geo, 0, 0);
    return;
}

static void
fnct_BdPolyFromText2 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BdPolyFromText(WKT encoded MULTILINESTRING, SRID)
/
/ returns the current geometry [POLYGON] by parsing a WKT encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, -1);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    fnct_aux_polygonize (context, geo, 0, 0);
    return;
}

static void
fnct_BdMPolyFromText1 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ BdMPolyFromText(WKT encoded MULTILINESTRING)
/
/ returns the current geometry [MULTIPOLYGON] by parsing a WKT encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, -1);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    fnct_aux_polygonize (context, geo, 1, 1);
    return;
}

static void
fnct_BdMPolyFromText2 (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ BdMPolyFromText(WKT encoded MULTILINESTRING, SRID)
/
/ returns the current geometry [MULTIPOLYGON] by parsing a WKT encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    const unsigned char *text;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    text = sqlite3_value_text (argv[0]);
    geo = gaiaParseWkt (text, -1);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    fnct_aux_polygonize (context, geo, 1, 1);
    return;
}

static void
fnct_BdPolyFromWKB1 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BdPolyFromWKB(WKB encoded MULTILINESTRING)
/
/ returns the current geometry [POLYGON] by parsing a WKB encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    int n_bytes;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, -1))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    fnct_aux_polygonize (context, geo, 0, 0);
    return;
}

static void
fnct_BdPolyFromWKB2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BdPolyFromWKB(WKB encoded MULTILINESTRING)
/
/ returns the current geometry [POLYGON] by parsing a WKB encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    int n_bytes;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, -1))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    fnct_aux_polygonize (context, geo, 0, 0);
    return;
}

static void
fnct_BdMPolyFromWKB1 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BdMPolyFromWKB(WKB encoded MULTILINESTRING)
/
/ returns the current geometry [MULTIPOLYGON] by parsing a WKB encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    int n_bytes;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, -1))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = 0;
    fnct_aux_polygonize (context, geo, 1, 1);
    return;
}

static void
fnct_BdMPolyFromWKB2 (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ BdMPolyFromWKB(WKB encoded MULTILINESTRING)
/
/ returns the current geometry [MULTIPOLYGON] by parsing a WKB encoded MULTILINESTRING 
/ or NULL if any error is encountered
/
*/
    int n_bytes;
    const unsigned char *wkb;
    gaiaGeomCollPtr geo = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_null (context);
	  return;
      }
    wkb = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (!check_wkb (wkb, n_bytes, -1))
	return;
    geo = gaiaFromWkb (wkb, n_bytes);
    if (geo == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geo->DeclaredType != GAIA_MULTILINESTRING)
      {
	  gaiaFreeGeomColl (geo);
	  sqlite3_result_null (context);
	  return;
      }
    geo->Srid = sqlite3_value_int (argv[1]);
    fnct_aux_polygonize (context, geo, 1, 1);
    return;
}

static void
fnct_OffsetCurve (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ OffsetCurve(BLOBencoded geometry, DOUBLE radius)
/
/ returns a new geometry representing the OFFSET-CURVE for current geometry
/ [a LINESTRING is expected]
/ or NULL if any error is encountered
/
/ negative radius: right-side / positive radius: left-side
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    double radius;
    int int_value;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	radius = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  radius = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaOffsetCurve_r (data, geo, radius, 16, 0);
	  else
	      result = gaiaOffsetCurve (geo, radius, 16, 0);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SingleSidedBuffer (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ SingleSidedBuffer(BLOBencoded geometry, radius, left-or-right-side)
/
/ returns a new geometry representing the SingleSided BUFFER 
/ for current geometry [a LINESTRING is expected]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    double radius;
    int int_value;
    int left_right;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	radius = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  radius = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	left_right = sqlite3_value_int (argv[2]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaSingleSidedBuffer_r (data, geo, radius, 16, left_right);
	  else
	      result = gaiaSingleSidedBuffer (geo, radius, 16, left_right);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_HausdorffDistance (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ HausdorffDistance(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns the discrete Hausdorff distance between GEOM-1 and GEOM-2
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    double dist;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaHausdorffDistance_r (data, geo1, geo2, &dist);
	  else
	      ret = gaiaHausdorffDistance (geo1, geo2, &dist);
	  if (!ret)
	      sqlite3_result_null (context);
	  sqlite3_result_double (context, dist);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_SharedPaths (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SharedPaths(BLOBencoded geometry1, BLOBencoded geometry2)
/
/ returns a new geometry representing common (shared) Edges
/ [two LINESTRINGs/MULTILINESTRINGs are expected]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo1 == NULL || geo2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaSharedPaths_r (data, geo1, geo2);
	  else
	      result = gaiaSharedPaths (geo1, geo2);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo1->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Covers (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Covers(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if GEOM-1 "spatially covers" GEOM-2
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedCovers (data, geo1,
						blob1, bytes1, geo2, blob2,
						bytes2);
	  else
	      ret = gaiaGeomCollCovers (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_CoveredBy (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CoveredBy(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns:
/ 1 if GEOM-1 is "spatially covered by" GEOM-2
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *blob1;
    unsigned char *blob2;
    int bytes1;
    int bytes2;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    blob1 = (unsigned char *) sqlite3_value_blob (argv[0]);
    bytes1 = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (blob1, bytes1, gpkg_mode, gpkg_amphibious);
    blob2 = (unsigned char *) sqlite3_value_blob (argv[1]);
    bytes2 = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (blob2, bytes2, gpkg_mode, gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_int (context, -1);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      ret = gaiaGeomCollPreparedCoveredBy (data,
						   geo1, blob1, bytes1, geo2,
						   blob2, bytes2);
	  else
	      ret = gaiaGeomCollCoveredBy (geo1, geo2);
	  sqlite3_result_int (context, ret);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_LineInterpolatePoint (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ LineInterpolatePoint(BLOBencoded geometry1, double fraction)
/
/ returns a new geometry representing a point interpolated along a line
/ [a LINESTRING is expected / fraction ranging from 0.0 to 1.0]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double fraction;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	fraction = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  fraction = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaLineInterpolatePoint_r (data, geo, fraction);
	  else
	      result = gaiaLineInterpolatePoint (geo, fraction);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_LineInterpolateEquidistantPoints (sqlite3_context * context, int argc,
				       sqlite3_value ** argv)
{
/* SQL function:
/ LineInterpolateEquidistantPointS(BLOBencoded geometry1, double distance)
/
/ returns a new geometry representing a point interpolated along a line
/ [a LINESTRING is expected / fraction ranging from 0.0 to 1.0]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double distance;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	distance = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  distance = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaLineInterpolateEquidistantPoints_r (data, geo, distance);
	  else
	      result = gaiaLineInterpolateEquidistantPoints (geo, distance);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_LineLocatePoint (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ LineLocatePoint(BLOBencoded geometry1, BLOBencoded geometry2)
/
/ return a number (between 0.0 and 1.0) representing the location 
/ of the closest point on LineString to the given Point, as a fraction 
/ of total 2d line length
/
/ - geom1 is expected to represent some LINESTRING
/ - geom2 is expected to represent some POINT
*/
    unsigned char *p_blob;
    int n_bytes;
    double fraction;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo1 == NULL || geo2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      fraction = gaiaLineLocatePoint_r (data, geo1, geo2);
	  else
	      fraction = gaiaLineLocatePoint (geo1, geo2);
	  if (fraction >= 0.0 && fraction <= 1.0)
	      sqlite3_result_double (context, fraction);
	  else
	      sqlite3_result_null (context);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_LineSubstring (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LineSubstring(BLOBencoded geometry1, double start_fraction, double end_fraction)
/
/ Return a Linestring being a substring of the input one starting and ending at 
/ the given fractions of total 2d length [fractions ranging from 0.0 to 1.0]
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double fraction1;
    double fraction2;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	fraction1 = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  fraction1 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	fraction2 = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  fraction2 = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaLineSubstring_r (data, geo, fraction1, fraction2);
	  else
	      result = gaiaLineSubstring (geo, fraction1, fraction2);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ClosestPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ClosestPoint(BLOBencoded geometry1, BLOBencoded geometry2)
/
/ Returns the Point on geom1 that is closest to geom2
/ NULL is returned for invalid arguments (or if distance is ZERO)
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo1 == NULL || geo2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaShortestLine_r (data, geo1, geo2);
	  else
	      result = gaiaShortestLine (geo1, geo2);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else if (result->FirstLinestring == NULL)
	    {
		gaiaFreeGeomColl (result);
		sqlite3_result_null (context);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		double x;
		double y;
		double z;
		double m;
		int len;
		unsigned char *p_result = NULL;
		gaiaGeomCollPtr pt = NULL;
		gaiaLinestringPtr ln = result->FirstLinestring;
		if (ln->DimensionModel == GAIA_XY_Z)
		    pt = gaiaAllocGeomCollXYZ ();
		else if (ln->DimensionModel == GAIA_XY_M)
		    pt = gaiaAllocGeomCollXYM ();
		else if (ln->DimensionModel == GAIA_XY_Z_M)
		    pt = gaiaAllocGeomCollXYZM ();
		else
		    pt = gaiaAllocGeomColl ();
		if (ln->DimensionModel == GAIA_XY_Z)
		  {
		      gaiaGetPointXYZ (ln->Coords, 0, &x, &y, &z);
		      gaiaAddPointToGeomCollXYZ (pt, x, y, z);
		  }
		else if (ln->DimensionModel == GAIA_XY_M)
		  {
		      gaiaGetPointXYM (ln->Coords, 0, &x, &y, &m);
		      gaiaAddPointToGeomCollXYM (pt, x, y, m);
		  }
		else if (ln->DimensionModel == GAIA_XY_Z_M)
		  {
		      gaiaGetPointXYZM (ln->Coords, 0, &x, &y, &z, &m);
		      gaiaAddPointToGeomCollXYZM (pt, x, y, z, m);
		  }
		else
		  {
		      gaiaGetPoint (ln->Coords, 0, &x, &y);
		      gaiaAddPointToGeomColl (pt, x, y);
		  }
		pt->Srid = geo1->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (pt, &p_result, &len, gpkg_mode,
					    tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
		gaiaFreeGeomColl (pt);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_ShortestLine (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ShortestLine(BLOBencoded geometry1, BLOBencoded geometry2)
/
/ Returns the shortest line between two geometries
/ NULL is returned for invalid arguments (or if distance is ZERO)
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo1 == NULL || geo2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaShortestLine_r (data, geo1, geo2);
	  else
	      result = gaiaShortestLine (geo1, geo2);
	  sqlite3_result_null (context);
	  if (!result)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo1->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Snap (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Snap(BLOBencoded geometry1, BLOBencoded geometry2, double tolerance)
/
/ Returns a new Geometry corresponding to geom1 snapped to geom2
/ and using the given tolerance
/ NULL is returned for invalid arguments (or if distance is ZERO)
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double tolerance;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	tolerance = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[2]);
	  tolerance = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo1 == NULL || geo2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaSnap_r (data, geo1, geo2, tolerance);
	  else
	      result = gaiaSnap (geo1, geo2, tolerance);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo1->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_LineMerge (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LineMerge(BLOBencoded geometry)
/
/ Assuming that Geometry represents a set of sparse Linestrings,
/ this function will attempt to reassemble a single line
/ (or a set of lines)
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaLineMerge_r (data, geo);
	  else
	      result = gaiaLineMerge (geo);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_UnaryUnion (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ UnaryUnion(BLOBencoded geometry)
/
/ exactly like Union, but using a single Collection
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result = gaiaUnaryUnion_r (data, geo);
	  else
	      result = gaiaUnaryUnion (geo);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_SquareGrid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_SquareGrid(BLOBencoded geom, double size)
/ ST_SquareGrid(BLOBencoded geom, double size, int mode)
/ ST_SquareGrid(BLOBencoded geom, double size, int mode, BLOBencoded origin)
/
/ mode interpretation:
/ ==============================================
/ positive = a MULTILINESTRING will be returned
/        0 = a MULTIPOLYGON will be returned
/ negative = a MULTIPOINT will be returned
/ ==============================================
/
/ Builds a regular grid (Square cells) covering the geom.
/ each cell has the edges's length as defined by the size argument
/ an arbitrary origin is supported (0,0 is assumed by default)
/ return NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double origin_x = 0.0;
    double origin_y = 0.0;
    double size;
    int mode = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr point = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  size = int_value;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
      {
	  size = sqlite3_value_double (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (size <= 0.0)
      {
	  /* negative side size */
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      mode = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
	  n_bytes = sqlite3_value_bytes (argv[3]);
	  point =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
	  if (!point)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (point->FirstLinestring != NULL)
	      goto no_point;
	  if (point->FirstPolygon != NULL)
	      goto no_point;
	  if (point->FirstPoint != NULL)
	    {
		if (point->FirstPoint == point->LastPoint)
		  {
		      origin_x = point->FirstPoint->X;
		      origin_y = point->FirstPoint->Y;
		      gaiaFreeGeomColl (point);
		  }
		else
		    goto no_point;
	    }
	  else
	      goto no_point;

      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (geo->FirstPoint != NULL)
	      goto no_polygon;
	  if (geo->FirstLinestring != NULL)
	      goto no_polygon;
	  if (geo->FirstPolygon == NULL)
	      goto no_polygon;
	  if (data != NULL)
	      result =
		  gaiaSquareGrid_r (data, geo, origin_x, origin_y, size, mode);
	  else
	      result = gaiaSquareGrid (geo, origin_x, origin_y, size, mode);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
    return;

  no_point:
    gaiaFreeGeomColl (point);
    sqlite3_result_null (context);
    return;

  no_polygon:
    gaiaFreeGeomColl (geo);
    sqlite3_result_null (context);
    return;
}

static void
fnct_TriangularGrid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_TriangularGrid(BLOBencoded geom, double size)
/ ST_TriangularGrid(BLOBencoded geom, double size, int mode)
/ ST_TriangularGrid(BLOBencoded geom, double size, int mode, BLOBencoded origin)
/
/ mode interpretation:
/ ==============================================
/ positive = a MULTILINESTRING will be returned
/        0 = a MULTIPOLYGON will be returned
/ negative = a MULTIPOINT will be returned
/ ==============================================
/
/ Builds a regular grid (Triangular cells) covering the geom.
/ each cell has the edge's length as defined by the size argument
/ an arbitrary origin is supported (0,0 is assumed by default)
/ return NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double origin_x = 0.0;
    double origin_y = 0.0;
    double size;
    int mode = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr point = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  size = int_value;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
      {
	  size = sqlite3_value_double (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (size <= 0.0)
      {
	  /* negative side size */
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      mode = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
	  n_bytes = sqlite3_value_bytes (argv[3]);
	  point =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
	  if (!point)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (point->FirstLinestring != NULL)
	      goto no_point;
	  if (point->FirstPolygon != NULL)
	      goto no_point;
	  if (point->FirstPoint != NULL)
	    {
		if (point->FirstPoint == point->LastPoint)
		  {
		      origin_x = point->FirstPoint->X;
		      origin_y = point->FirstPoint->Y;
		      gaiaFreeGeomColl (point);
		  }
		else
		    goto no_point;
	    }
	  else
	      goto no_point;

      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (geo->FirstPoint != NULL)
	      goto no_polygon;
	  if (geo->FirstLinestring != NULL)
	      goto no_polygon;
	  if (geo->FirstPolygon == NULL)
	      goto no_polygon;
	  if (data != NULL)
	      result =
		  gaiaTriangularGrid_r (data, geo, origin_x, origin_y, size,
					mode);
	  else
	      result = gaiaTriangularGrid (geo, origin_x, origin_y, size, mode);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
    return;

  no_point:
    gaiaFreeGeomColl (point);
    sqlite3_result_null (context);
    return;

  no_polygon:
    gaiaFreeGeomColl (geo);
    sqlite3_result_null (context);
    return;
}

static void
fnct_HexagonalGrid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_HexagonalGrid(BLOBencoded geom, double size)
/ ST_HexagonalGrid(BLOBencoded geom, double size, int mode)
/ ST_HexagonalGrid(BLOBencoded geom, double size, int mode, BLOBencoded origin)
/
/ mode interpretation:
/ ==============================================
/ positive = a MULTILINESTRING will be returned
/        0 = a MULTIPOLYGON will be returned
/ negative = a MULTIPOINT will be returned
/ ==============================================
/
/ Builds a regular grid (Hexagonal cells) covering the geom.
/ each cell has the edges's length as defined by the size argument
/ an arbitrary origin is supported (0,0 is assumed by default)
/ return NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double origin_x = 0.0;
    double origin_y = 0.0;
    double size;
    int mode = 0;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr point = NULL;
    gaiaGeomCollPtr result = NULL;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  size = int_value;
      }
    else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
      {
	  size = sqlite3_value_double (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (size <= 0.0)
      {
	  /* negative side size */
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      mode = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_BLOB)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
	  n_bytes = sqlite3_value_bytes (argv[3]);
	  point =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
	  if (!point)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (point->FirstLinestring != NULL)
	      goto no_point;
	  if (point->FirstPolygon != NULL)
	      goto no_point;
	  if (point->FirstPoint != NULL)
	    {
		if (point->FirstPoint == point->LastPoint)
		  {
		      origin_x = point->FirstPoint->X;
		      origin_y = point->FirstPoint->Y;
		      gaiaFreeGeomColl (point);
		  }
		else
		    goto no_point;
	    }
	  else
	      goto no_point;

      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (geo->FirstPoint != NULL)
	      goto no_polygon;
	  if (geo->FirstLinestring != NULL)
	      goto no_polygon;
	  if (geo->FirstPolygon == NULL)
	      goto no_polygon;
	  if (data != NULL)
	      result =
		  gaiaHexagonalGrid_r (data, geo, origin_x, origin_y, size,
				       mode);
	  else
	      result = gaiaHexagonalGrid (geo, origin_x, origin_y, size, mode);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
    return;

  no_point:
    gaiaFreeGeomColl (point);
    sqlite3_result_null (context);
    return;

  no_polygon:
    gaiaFreeGeomColl (geo);
    sqlite3_result_null (context);
    return;
}

static void
fnct_LinesCutAtNodes (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ LinesCutAtNodes(BLOBencoded geometry1, BLOBencoded geometry2)
/
/ Assuming that Geometry-1 represents a set of arbitray Linestrings,
/ and that Geometry-2 represents of arbitrary Points, this function 
/ will then attempt to cut lines accordingly to given nodes.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom1 == NULL || geom2 == NULL)
      {
	  if (geom1)
	      gaiaFreeGeomColl (geom1);
	  if (geom2)
	      gaiaFreeGeomColl (geom2);
	  sqlite3_result_null (context);
	  return;
      }
    result = gaiaLinesCutAtNodes (geom1, geom2);
    if (result == NULL)
      {
	  sqlite3_result_null (context);
      }
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  result->Srid = geom1->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
    gaiaFreeGeomColl (geom1);
    gaiaFreeGeomColl (geom2);
}

static int
cmp_pt_coords (const void *p1, const void *p2)
{
/* compares two nodes  by ID [for QSORT] */
    gaiaPointPtr pt1 = *((gaiaPointPtr *) p1);
    gaiaPointPtr pt2 = *((gaiaPointPtr *) p2);
    if (pt1->X == pt2->X && pt1->Y == pt2->Y && pt1->Z == pt2->Z)
	return 0;
    if (pt1->X > pt2->X)
	return 1;
    if (pt1->X == pt2->X && pt1->Y > pt2->Y)
	return 1;
    if (pt1->X == pt2->X && pt1->Y == pt2->Y && pt1->Z > pt2->Z)
	return 1;
    return -1;
}

static gaiaGeomCollPtr
auxPolygNodes (gaiaGeomCollPtr geom)
{
/* attempting to identify Ring-Nodes */
    gaiaGeomCollPtr result = NULL;
    gaiaPolygonPtr pg;
    gaiaRingPtr rng;
    gaiaPointPtr pt;
    gaiaPointPtr prev_pt;
    gaiaPointPtr *sorted = NULL;
    int count = 0;
    int iv;
    int ib;
    double x;
    double y;
    double z;
    double m;

/* inserting all Points into a Dynamic Line */
    gaiaDynamicLinePtr dyn = gaiaAllocDynamicLine ();
    pg = geom->FirstPolygon;
    while (pg)
      {
	  rng = pg->Exterior;
	  /* CAVEAT: first point needs to be skipped (closed ring) */
	  for (iv = 1; iv < rng->Points; iv++)
	    {
		/* exterior ring */
		if (geom->DimensionModel == GAIA_XY_Z)
		  {
		      gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z);
		      gaiaAppendPointZToDynamicLine (dyn, x, y, z);
		  }
		else if (geom->DimensionModel == GAIA_XY_M)
		  {
		      gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m);
		      gaiaAppendPointMToDynamicLine (dyn, x, y, m);
		  }
		else if (geom->DimensionModel == GAIA_XY_Z_M)
		  {
		      gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m);
		      gaiaAppendPointZMToDynamicLine (dyn, x, y, z, m);
		  }
		else
		  {
		      gaiaGetPoint (rng->Coords, iv, &x, &y);
		      gaiaAppendPointToDynamicLine (dyn, x, y);
		  }
	    }

	  for (ib = 0; ib < pg->NumInteriors; ib++)
	    {
		rng = pg->Interiors + ib;
		/* CAVEAT: first point needs to be skipped (closed ring) */
		for (iv = 1; iv < rng->Points; iv++)
		  {
		      /* interior ring */
		      if (geom->DimensionModel == GAIA_XY_Z)
			{
			    gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z);
			    gaiaAppendPointZToDynamicLine (dyn, x, y, z);
			}
		      else if (geom->DimensionModel == GAIA_XY_M)
			{
			    gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m);
			    gaiaAppendPointMToDynamicLine (dyn, x, y, m);
			}
		      else if (geom->DimensionModel == GAIA_XY_Z_M)
			{
			    gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m);
			    gaiaAppendPointZMToDynamicLine (dyn, x, y, z, m);
			}
		      else
			{
			    gaiaGetPoint (rng->Coords, iv, &x, &y);
			    gaiaAppendPointToDynamicLine (dyn, x, y);
			}
		  }
	    }
	  pg = pg->Next;
      }

    pt = dyn->First;
    while (pt)
      {
	  /* counting how many points */
	  count++;
	  pt = pt->Next;
      }
    if (count == 0)
      {
	  gaiaFreeDynamicLine (dyn);
	  return NULL;
      }

/* allocating and initializing an array of pointers */
    sorted = malloc (sizeof (gaiaPointPtr) * count);
    iv = 0;
    pt = dyn->First;
    while (pt)
      {
	  *(sorted + iv++) = pt;
	  pt = pt->Next;
      }

/* sorting points by coords */
    qsort (sorted, count, sizeof (gaiaPointPtr), cmp_pt_coords);

    if (geom->DimensionModel == GAIA_XY_Z)
	result = gaiaAllocGeomCollXYZ ();
    else if (geom->DimensionModel == GAIA_XY_M)
	result = gaiaAllocGeomCollXYM ();
    else if (geom->DimensionModel == GAIA_XY_Z_M)
	result = gaiaAllocGeomCollXYZM ();
    else
	result = gaiaAllocGeomColl ();
    result->Srid = geom->Srid;

/* identifying nodes */
    prev_pt = NULL;
    for (iv = 0; iv < count; iv++)
      {
	  pt = *(sorted + iv);
	  if (prev_pt != NULL)
	    {
		if (prev_pt->X == pt->X && prev_pt->Y == pt->Y
		    && prev_pt->Z == pt->Z)
		  {
		      if (result->LastPoint != NULL)
			{
			    if (result->LastPoint->X == pt->X
				&& result->LastPoint->Y == pt->Y
				&& result->LastPoint->Z == pt->Z)
				continue;
			}
		      /* Node found */
		      if (result->DimensionModel == GAIA_XY_Z)
			  gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y,
						     pt->Z);
		      else if (result->DimensionModel == GAIA_XY_M)
			  gaiaAddPointToGeomCollXYM (result, pt->X, pt->Y,
						     pt->M);
		      else if (result->DimensionModel == GAIA_XY_Z_M)
			  gaiaAddPointToGeomCollXYZM (result, pt->X,
						      pt->Y, pt->Z, pt->M);
		      else
			  gaiaAddPointToGeomColl (result, pt->X, pt->Y);
		  }
	    }
	  prev_pt = pt;
      }

    if (result->FirstPoint == NULL)
      {
	  gaiaFreeGeomColl (result);
	  result = NULL;
      }
    free (sorted);
    gaiaFreeDynamicLine (dyn);
    return result;
}

static void
fnct_RingsCutAtNodes (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ RingsCutAtNodes(BLOBencoded geometry)
/
/ This function will attempt to return a collection of lines
/ representing Polygon/Rings: the input geometry is expected
/ to be a Polygon or MultiPolygon.
/ Each Ring will be cut accordingly to any identified "node"
/ i.e. self-intersections or intersections between two Rings.
/
/ NULL is returned for invalid arguments
*/
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    unsigned char *p_blob;
    int n_bytes;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;
    gaiaGeomCollPtr geom = NULL;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* checking if Geometry is a Polygon or MultiPolyhon */
    pt = geom->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    ln = geom->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    pg = geom->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pts > 0 || lns > 0 || pgs == 0)
      {
	  /* not Polygon/MultiPolygon */
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_null (context);
	  return;
      }

/* transforming Rings into Linestrings */
    geom1 = gaiaLinearize (geom, 1);
    if (geom1 == NULL)
      {
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_null (context);
	  return;
      }

/* identifying the Nodes */
    geom2 = auxPolygNodes (geom);
    if (geom2 == NULL)
      {
	  /* there is no need to cut any Ring [no Nodes] */
	  int len;
	  unsigned char *p_result = NULL;
	  geom1->Srid = geom->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (geom1, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (geom);
	  gaiaFreeGeomColl (geom1);
	  return;
      }

/* attempting to cut Rings */
    result = gaiaLinesCutAtNodes (geom1, geom2);
    if (result == NULL)
	sqlite3_result_null (context);
    else
      {
	  /* builds the BLOB geometry to be returned */
	  int len;
	  unsigned char *p_result = NULL;
	  result->Srid = geom->Srid;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_result, len, free);
	  gaiaFreeGeomColl (result);
      }
    gaiaFreeGeomColl (geom);
    gaiaFreeGeomColl (geom1);
    gaiaFreeGeomColl (geom2);
}

#ifdef GEOS_ADVANCED		/* GEOS advanced features - 3.4.0 */

static void
fnct_DelaunayTriangulation (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ DelaunayTriangulation(BLOBencoded geometry)
/ DelaunayTriangulation(BLOBencoded geometry, boolean onlyEdges)
/ DelaunayTriangulation(BLOBencoded geometry, boolean onlyEdges, double tolerance)
/
/ Attempts to build a Delaunay Triangulation using all points/vertices 
/ found in the input geometry.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int int_value;
    double tolerance = 0.0;
    int only_edges = 0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      only_edges = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		tolerance = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaDelaunayTriangulation_r (data, geo, tolerance,
					       only_edges);
	  else
	      result = gaiaDelaunayTriangulation (geo, tolerance, only_edges);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_VoronojDiagram (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ VoronojDiagram(BLOBencoded geometry)
/ VoronojDiagram(BLOBencoded geometry, boolean onlyEdges)
/ VoronojDiagram(BLOBencoded geometry, boolean onlyEdges, 
/        double extra_frame_size)
/ VoronojDiagram(BLOBencoded geometry, boolean onlyEdges,
/        double extra_frame_size, double tolerance)
/
/ Attempts to build a Voronoj Diagram using all points/vertices 
/ found in the input geometry.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int int_value;
    double tolerance = 0.0;
    double extra_frame_size = -1.0;
    int only_edges = 0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      only_edges = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      extra_frame_size = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		extra_frame_size = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		tolerance = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaVoronojDiagram_r (data, geo, extra_frame_size,
					tolerance, only_edges);
	  else
	      result =
		  gaiaVoronojDiagram (geo, extra_frame_size, tolerance,
				      only_edges);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_ConcaveHull (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ConcaveHull(BLOBencoded geometry)
/ ConcaveHull(BLOBencoded geometry, double factor)
/ ConcaveHull(BLOBencoded geometry, double factor, boolean allow_holes)
/ ConcaveHull(BLOBencoded geometry, double factor,
/        boolean allow_holes, double tolerance)
/
/ Attempts to build a ConcaveHull using all points/vertices 
/ found in the input geometry.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int int_value;
    double tolerance = 0.0;
    double factor = 3.0;
    int allow_holes = 0;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      factor = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		factor = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      allow_holes = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		tolerance = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  void *data = sqlite3_user_data (context);
	  if (data != NULL)
	      result =
		  gaiaConcaveHull_r (data, geo, factor, tolerance, allow_holes);
	  else
	      result = gaiaConcaveHull (geo, factor, tolerance, allow_holes);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

#endif /* end GEOS advanced features */

#ifdef ENABLE_RTTOPO		/* enabling RTTOPO support */

static void
fnct_RTTOPO_GetLastWarningMsg (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ RTTOPO_GetLastWarningMsg()
/
/ return the most recent RTTOPO warning message (if any)
/ return NULL on any other case
*/
    const char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    msg = gaiaGetRtTopoWarningMsg (cache);
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_RTTOPO_GetLastErrorMsg (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ RTTOPO_GetLastErrorMsg()
/
/ return the most recent RTEOM error message (if any)
/ return NULL on any other case
*/
    const char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    msg = gaiaGetRtTopoErrorMsg (cache);
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_MakeValid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MakeValid(BLOBencoded geometry)
/
/ Attempts to make an invalid geometry valid without loosing vertices.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaMakeValid (cache, geo);
	  if (result == NULL)
	    {
		char *msg;
		const char *lw_err = gaiaGetRtTopoErrorMsg (cache);
		if (lw_err)
		    msg = sqlite3_mprintf
			("MakeValid error - RTTOPO reports: %s\n", lw_err);
		else
		    msg = sqlite3_mprintf
			("MakeValid error - RTTOPO reports: Unknown Reason\n");
		sqlite3_result_error (context, msg, strlen (msg));
		sqlite3_free (msg);
	    }
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_MakeValidDiscarded (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ MakeValidDiscarded(BLOBencoded geometry)
/
/ Strictly related to MakeValid(); useful to collect any offending item
/ discarded during the validation process.
/ NULL is returned for invalid arguments (or if no discarded items are found)
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaMakeValidDiscarded (cache, geo);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Segmentize (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Segmentize(BLOBencoded geometry, double dist)
/
/ Ensure every segment is at most 'dist' long
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    int int_value;
    double dist;
    gaiaGeomCollPtr geo = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	dist = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  dist = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geo == NULL)
	sqlite3_result_null (context);
    else
      {
	  result = gaiaSegmentize (cache, geo, dist);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = geo->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (geo);
}

static void
fnct_Split (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Split(BLOBencoded input, BLOBencoded blade)
/
/ Returns a collection of geometries resulting by splitting a geometry
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr input = NULL;
    gaiaGeomCollPtr blade = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    input =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (input == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    blade =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (blade == NULL)
      {
	  gaiaFreeGeomColl (input);
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  result = gaiaSplit (cache, input, blade);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = input->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (input);
    gaiaFreeGeomColl (blade);
}

static void
fnct_SplitLeft (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SplitLeft(BLOBencoded input, BLOBencoded blade)
/
/ Returns a collection of geometries resulting by splitting a geometry [left half]
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr input = NULL;
    gaiaGeomCollPtr blade = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    input =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (input == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    blade =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (blade == NULL)
      {
	  gaiaFreeGeomColl (input);
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  result = gaiaSplitLeft (cache, input, blade);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = input->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (input);
    gaiaFreeGeomColl (blade);
}

static void
fnct_SplitRight (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SplitRight(BLOBencoded input, BLOBencoded blade)
/
/ Returns a collection of geometries resulting by splitting a geometry [right half]
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr input = NULL;
    gaiaGeomCollPtr blade = NULL;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    input =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (input == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    blade =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (blade == NULL)
      {
	  gaiaFreeGeomColl (input);
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  result = gaiaSplitRight (cache, input, blade);
	  if (result == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		/* builds the BLOB geometry to be returned */
		int len;
		unsigned char *p_result = NULL;
		result->Srid = input->Srid;
		gaiaToSpatiaLiteBlobWkbEx2 (result, &p_result, &len,
					    gpkg_mode, tiny_point);
		sqlite3_result_blob (context, p_result, len, free);
		gaiaFreeGeomColl (result);
	    }
      }
    gaiaFreeGeomColl (input);
    gaiaFreeGeomColl (blade);
}

static int
getXYSinglePoint (gaiaGeomCollPtr geom, double *x, double *y)
{
/* check if this geometry is a simple Point (returning 2D coords) */
    double z;
    double m;
    return getXYZMSinglePoint (geom, x, y, &z, &m);
}

static void
fnct_Azimuth (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Azimuth(BLOBencoded pointA, BLOBencoded pointB)
/
/ Returns the angle in radians from the horizontal of the vector 
/ defined by pointA and pointB. 
/ Angle is computed clockwise from down-to-up: on the clock: 
/ 12=0; 3=PI/2; 6=PI; 9=3PI/2.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    double x1;
    double y1;
    double x2;
    double y2;
    double a;
    double b;
    double rf;
    double azimuth;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving and validating the first point */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!getXYSinglePoint (geom, &x1, &y1))
      {
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_null (context);
	  return;
      }
    srid = geom->Srid;
    gaiaFreeGeomColl (geom);

/* retrieving and validating the second point */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!getXYSinglePoint (geom, &x2, &y2))
      {
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_null (context);
	  return;
      }
    gaiaFreeGeomColl (geom);

    if (getEllipsoidParams (sqlite, srid, &a, &b, &rf))
      {
	  if (gaiaEllipsoidAzimuth (cache, x1, y1, x2, y2, a, b, &azimuth))
	      sqlite3_result_double (context, azimuth);
	  else
	      sqlite3_result_null (context);
	  return;
      }

    if (gaiaAzimuth (cache, x1, y1, x2, y2, &azimuth))
	sqlite3_result_double (context, azimuth);
    else
	sqlite3_result_null (context);
}

static void
fnct_Project (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ Project(BLOBencoded point, distance Double, bearing Double)
/
/ Returns a new Point projected from a start point given a
/ distance and a bearing. 
/ - Point is expected to be Long/Lat
/ - Distance is in meters
/ - Bearing is in radians; on the clock: 
/   12=0; 3=PI/2; 6=PI; 9=3PI/2.
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    double x1;
    double y1;
    double x2;
    double y2;
    int ival;
    double distance;
    double azimuth;
    double a;
    double b;
    double rf;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	distance = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[1]);
	  distance = ival;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	azimuth = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  ival = sqlite3_value_int (argv[2]);
	  azimuth = ival;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving and validating the start point */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!getXYSinglePoint (geom, &x1, &y1))
      {
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_null (context);
	  return;
      }
    srid = geom->Srid;
    gaiaFreeGeomColl (geom);
    if (!getEllipsoidParams (sqlite, srid, &a, &b, &rf))
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (distance == 0.0)
      {
	  /* returning the Start Point */
	  gaiaMakePointEx (tiny_point, x1, y1, srid, &p_blob, &n_bytes);
	  if (!p_blob)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_blob, n_bytes, free);
	  return;
      }

    if (gaiaProjectedPoint (cache, x1, y1, a, b, distance, azimuth, &x2, &y2))
      {
	  gaiaMakePointEx (tiny_point, x2, y2, srid, &p_blob, &n_bytes);
	  if (!p_blob)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_blob (context, p_blob, n_bytes, free);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_GeoHash (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeoHash(BLOBencoded geom)
/ GeoHash(BLOBencoded geom, Integer precision)
/
/ Returns a GeoHash representation for input geometry
/ (expected to be in longitude/latitude coords)
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    int precision = 0;
    char *geo_hash;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo_hash = gaiaGeoHash (cache, geom, precision);
    if (geo_hash != NULL)
      {
	  int len = strlen (geo_hash);
	  sqlite3_result_text (context, geo_hash, len, free);
      }
    else
	sqlite3_result_null (context);
    gaiaFreeGeomColl (geom);
}

static char *
get_srs_by_srid (sqlite3 * sqlite, int srid, int longsrs)
{
/* retrieves the short- or long- srs reference for the given srid */
    char sql[1024];
    int ret;
    const char *name;
    int i;
    char **results;
    int rows;
    int columns;
    int len;
    char *srs = NULL;

    if (longsrs)
	sprintf (sql,
		 "SELECT 'urn:ogc:def:crs:' || auth_name || '::' || auth_srid "
		 "FROM spatial_ref_sys WHERE srid = %d", srid);
    else
	sprintf (sql, "SELECT auth_name || ':' || auth_srid "
		 "FROM spatial_ref_sys WHERE srid = %d", srid);
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	return NULL;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		name = results[(i * columns) + 0];
		len = strlen (name);
		srs = malloc (len + 1);
		strcpy (srs, name);
	    }
      }
    sqlite3_free_table (results);
    return srs;
}

static void
fnct_AsX3D (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ AsX3D(BLOBencoded geom)
/ AsX3D(BLOBencoded geom, Integer precision)
/ AsX3D(BLOBencoded geom, Integer precision, Integer options)
/ AsX3D(BLOBencoded geom, Integer precision, Integer options, Text refid)
/
/ Returns an X3D representation for input geometry
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    int precision = 15;
    int options = 0;
    const char *refid = "";
    char *srs = NULL;
    char *x3d;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      options = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	      refid = (const char *) sqlite3_value_text (argv[3]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (geom == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (geom->Srid > 0)
      {
	  int longshort = 0;
	  if (options & 1)
	      longshort = 1;
	  srs = get_srs_by_srid (sqlite, geom->Srid, longshort);
      }
    x3d = gaiaAsX3D (cache, geom, srs, precision, options, refid);
    if (x3d != NULL)
      {
	  int len = strlen (x3d);
	  sqlite3_result_text (context, x3d, len, free);
      }
    else
	sqlite3_result_null (context);
    gaiaFreeGeomColl (geom);
    if (srs)
	free (srs);
}

static void
fnct_3DDistance (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ 3DDistance(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns the 3D distance between GEOM-1 and GEOM-2
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    double dist;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  ret = gaia3DDistance (cache, geo1, geo2, &dist);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, dist);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_MaxDistance (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MaxDistance(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns the max 2D distance between GEOM-1 and GEOM-2
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    double dist;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  ret = gaiaMaxDistance (cache, geo1, geo2, &dist);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, dist);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_3DMaxDistance (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ 3DMaxDistance(BLOBencoded geom1, BLOBencoded geom2)
/
/ returns the max 3D distance between GEOM-1 and GEOM-2
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geo1 = NULL;
    gaiaGeomCollPtr geo2 = NULL;
    double dist;
    int ret;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geo2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo1 || !geo2)
	sqlite3_result_null (context);
    else
      {
	  ret = gaia3DMaxDistance (cache, geo1, geo2, &dist);
	  if (!ret)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, dist);
      }
    gaiaFreeGeomColl (geo1);
    gaiaFreeGeomColl (geo2);
}

static void
fnct_Node (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Node(BLOBencoded linestring(s))
/
/ Returns a new new (Multi)Linestring by re-noding the input linestring(s)
/ NULL is returned for invalid arguments
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr input;
    gaiaGeomCollPtr result;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving the input geometry */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    input =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (input == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

    result = gaiaNodeLines (cache, input);
    if (result != NULL)
      {
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_blob, &n_bytes, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_blob, n_bytes, free);
	  gaiaFreeGeomColl (result);
      }
    else
	sqlite3_result_null (context);
    gaiaFreeGeomColl (input);
}

static int
check_all_linestrings (gaiaGeomCollPtr in)
{
/* check id this is a collection of Linestrings */
    int pts = 0;
    int lns = 0;
    int pgs = 0;
    gaiaPointPtr pt;
    gaiaLinestringPtr ln;
    gaiaPolygonPtr pg;

    if (in == NULL)
	return 0;
/* checking if we have any POINT */
    pt = in->FirstPoint;
    while (pt)
      {
	  pts++;
	  pt = pt->Next;
      }
    if (pts > 0)
	return 0;
/* checking if we have any POLYGON */
    pg = in->FirstPolygon;
    while (pg)
      {
	  pgs++;
	  pg = pg->Next;
      }
    if (pgs > 0)
	return 0;
/* checking if we have any LINESTRING */
    ln = in->FirstLinestring;
    while (ln)
      {
	  lns++;
	  ln = ln->Next;
      }
    if (lns == 0)
	return 0;
    return 1;
}

static gaiaGeomCollPtr
get_nodes (gaiaGeomCollPtr in)
{
/* extracts all Nodes (Linestring extermities) */
    int iv;
    double x;
    double y;
    double m;
    double z;
    gaiaLinestringPtr ln;
    gaiaGeomCollPtr out;

    if (in == NULL)
	return NULL;

    if (in->DimensionModel == GAIA_XY_M)
	out = gaiaAllocGeomCollXYM ();
    else if (in->DimensionModel == GAIA_XY_Z)
	out = gaiaAllocGeomCollXYZ ();
    else if (in->DimensionModel == GAIA_XY_Z_M)
	out = gaiaAllocGeomCollXYZM ();
    else
	out = gaiaAllocGeomColl ();
    out->Srid = in->Srid;

    ln = in->FirstLinestring;
    while (ln)
      {
	  /* saving the extreme points - Start */
	  if (ln->DimensionModel == GAIA_XY_Z)
	    {
		gaiaGetPointXYZ (ln->Coords, 0, &x, &y, &z);
		gaiaAddPointToGeomCollXYZ (out, x, y, z);
	    }
	  else if (ln->DimensionModel == GAIA_XY_M)
	    {
		gaiaGetPointXYM (ln->Coords, 0, &x, &y, &m);
		gaiaAddPointToGeomCollXYM (out, x, y, m);
	    }
	  else if (ln->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaGetPointXYZM (ln->Coords, 0, &x, &y, &z, &m);
		gaiaAddPointToGeomCollXYZM (out, x, y, z, m);
	    }
	  else
	    {
		gaiaGetPoint (ln->Coords, 0, &x, &y);
		gaiaAddPointToGeomColl (out, x, y);
	    }
	  /* saving the extreme points - End */
	  iv = ln->Points - 1;
	  if (ln->DimensionModel == GAIA_XY_Z)
	    {
		gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
		gaiaAddPointToGeomCollXYZ (out, x, y, z);
	    }
	  else if (ln->DimensionModel == GAIA_XY_M)
	    {
		gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
		gaiaAddPointToGeomCollXYM (out, x, y, m);
	    }
	  else if (ln->DimensionModel == GAIA_XY_Z_M)
	    {
		gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
		gaiaAddPointToGeomCollXYZM (out, x, y, z, m);
	    }
	  else
	    {
		gaiaGetPoint (ln->Coords, iv, &x, &y);
		gaiaAddPointToGeomColl (out, x, y);
	    }
	  ln = ln->Next;
      }

    return out;
}

static int
point_is_defined (gaiaPointPtr in, gaiaGeomCollPtr geom)
{
/* checking if a Point is already defined */
    gaiaPointPtr pt = geom->FirstPoint;
    while (pt)
      {
	  if (geom->DimensionModel == GAIA_XY_Z)
	    {
		if (pt->X == in->X && pt->Y == in->Y && pt->Z == in->Z)
		    return 1;
	    }
	  else if (geom->DimensionModel == GAIA_XY_M)
	    {
		if (pt->X == in->X && pt->Y == in->Y && pt->M == in->M)
		    return 1;
	    }
	  else if (geom->DimensionModel == GAIA_XY_Z_M)
	    {
		if (pt->X == in->X && pt->Y == in->Y && pt->Z == in->Z
		    && pt->M == in->M)
		    return 1;
	    }
	  else
	    {
		if (pt->X == in->X && pt->Y == in->Y)
		    return 1;
	    }
	  pt = pt->Next;
      }
    return 0;
}

static gaiaGeomCollPtr
get_self_intersections (gaiaGeomCollPtr in_old, gaiaGeomCollPtr in_new)
{
/* extracting the self-intersection points */
    gaiaPointPtr pt;
    gaiaGeomCollPtr out;

    if (in_old->DimensionModel == GAIA_XY_M)
	out = gaiaAllocGeomCollXYM ();
    else if (in_old->DimensionModel == GAIA_XY_Z)
	out = gaiaAllocGeomCollXYZ ();
    else if (in_old->DimensionModel == GAIA_XY_Z_M)
	out = gaiaAllocGeomCollXYZM ();
    else
	out = gaiaAllocGeomColl ();
    out->Srid = in_old->Srid;

    pt = in_new->FirstPoint;
    while (pt)
      {
	  int ok1 = point_is_defined (pt, in_old);
	  int ok2 = point_is_defined (pt, out);
	  if (!ok1 && !ok2)
	    {
		/* inserting a Point into the result collection */
		if (out->DimensionModel == GAIA_XY_Z)
		    gaiaAddPointToGeomCollXYZ (out, pt->X, pt->Y, pt->Z);
		else if (out->DimensionModel == GAIA_XY_M)
		    gaiaAddPointToGeomCollXYM (out, pt->X, pt->Y, pt->M);
		else if (out->DimensionModel == GAIA_XY_Z_M)
		    gaiaAddPointToGeomCollXYZM (out, pt->X, pt->Y, pt->Z,
						pt->M);
		else
		    gaiaAddPointToGeomColl (out, pt->X, pt->Y);
	    }
	  pt = pt->Next;
      }

    if (out->FirstPoint == NULL)
      {
	  /* no self-intersections were found */
	  gaiaFreeGeomColl (out);
	  return NULL;
      }

    return out;
}

static void
fnct_SelfIntersections (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ST_SelfIntersections(BLOBencoded linestring(s))
/
/ Returns a MultiPoint Geometry representing any self-intersection
/ found within the input geometry [linestring(s)]
/ NULL is returned for invalid arguments, or when no self-intersections
/ were found
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr input;
    gaiaGeomCollPtr noded;
    gaiaGeomCollPtr result;
    gaiaGeomCollPtr nodes_in;
    gaiaGeomCollPtr nodes_out;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving the input geometry */
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    input =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (input == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* checking the input (Linestrings only) */
    if (!check_all_linestrings (input))
      {
	  gaiaFreeGeomColl (input);
	  sqlite3_result_null (context);
	  return;
      }
/* extracting all input nodes */
    nodes_in = get_nodes (input);

    noded = gaiaNodeLines (cache, input);
    gaiaFreeGeomColl (input);
/* extracting all output nodes */
    nodes_out = get_nodes (noded);
    gaiaFreeGeomColl (noded);

/* identifying the intersections */
    result = get_self_intersections (nodes_in, nodes_out);
    gaiaFreeGeomColl (nodes_in);
    gaiaFreeGeomColl (nodes_out);
    if (result != NULL)
      {
	  result->DeclaredType = GAIA_MULTIPOINT;
	  gaiaToSpatiaLiteBlobWkbEx2 (result, &p_blob, &n_bytes, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, p_blob, n_bytes, free);
	  gaiaFreeGeomColl (result);
      }
    else
	sqlite3_result_null (context);
}

#endif /* end RTTOPO support */


static void
fnct_Cutter (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_Cutter(TEXT in_db_prefix, TEXT input_table, TEXT input_geom,
/              TEXT blade_db_prefix, TEXT blade_table, TEXT blade_geom,
/              TEXT output_table)
/ ST_Cutter(TEXT in_db_prefix, TEXT input_table, TEXT input_geom,
/              TEXT blade_db_prefix, TEXT blade_table, TEXT blade_geom,
/              TEXT output_table, INT transaction)
/ ST_Cutter(TEXT in_db_prefix, TEXT input_table, TEXT input_geom,
/              TEXT blade_db_prefix, TEXT blade_table, TEXT blade_geom,
/              TEXT output_table, INT transaction, INT ram_temp_store)
/
/ the "input" table-geometry is expected to be declared as POINT,
/ LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING or MULTIPOLYGON
/ and can be of any 2D or 3D dimension
/
/ the "blade" table-geometry is expected to be declared as POLYGON
/ or MULTIPOLYGON, and will always be casted to a pure(X,Y) dimension
/
/ the "output" table *must* not exists, and will be automatically
/ created within the MAIN database.
/ 
/ in_db_prefix and/or blade_db_prefix can eventually be NULL, and
/ in this case the MAIN db will be assumed
/
/ input_geom and/or blade_geom can eventually be NULL, and in this
/ case the geometry column name will be automatically determined.
/ anyway when a table defines two or more Geometries declaring a
/ NULL geometry name will cause a failure.
/
///////////////////////////////////////////////////////////////////
/
/ will precisely cut the input dataset against polygonal blade(s)
/ and will consequently create and populate an output dataset
/
/
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    sqlite3 *sqlite;
    int ret = 0;
    const char *in_db_prefix = NULL;
    const char *input_table = NULL;
    const char *input_geom = NULL;
    const char *blade_db_prefix = NULL;
    const char *blade_table = NULL;
    const char *blade_geom = NULL;
    const char *output_table = NULL;
    int transaction = 0;
    int ram_tmp_store = 0;
    char **message = NULL;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	message = &(cache->cutterMessage);

    if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	in_db_prefix = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	input_table = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	input_geom = (const char *) sqlite3_value_text (argv[2]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	blade_db_prefix = (const char *) sqlite3_value_text (argv[3]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	blade_table = (const char *) sqlite3_value_text (argv[4]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
	blade_geom = (const char *) sqlite3_value_text (argv[5]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[6]) == SQLITE_TEXT)
	output_table = (const char *) sqlite3_value_text (argv[6]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 8)
      {
	  if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
	      transaction = sqlite3_value_int (argv[7]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    if (argc == 9)
      {
	  if (sqlite3_value_type (argv[8]) == SQLITE_INTEGER)
	      ram_tmp_store = sqlite3_value_int (argv[8]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }

    sqlite = sqlite3_context_db_handle (context);
    ret =
	gaiaCutter (sqlite, cache, in_db_prefix, input_table, input_geom,
		    blade_db_prefix, blade_table, blade_geom, output_table,
		    transaction, ram_tmp_store, message);

    sqlite3_result_int (context, ret);
}

static void
fnct_GetCutterMessage (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ GetCutterMessage( void )
/
/ will return the last diagnostic message from Cutter
/ NULL if there is no pending message
*/
    char *message = NULL;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
	message = cache->cutterMessage;

    if (message == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, message, strlen (message), SQLITE_STATIC);
}

static void
fnct_DrapeLine (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ST_DrapeLine( line-1 Linestring, line-2 Linestring )
/    or
/ ST_DrapeLine( line-1 Linestring, line-2 Linestring, tolerance Double )
/
/ will drape line-1 over line-2 returning a new linestring
/ NULL on invalid arguments or if any error is encountered
*/
    unsigned char *blob;
    int bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    gaiaGeomCollPtr geom3 = NULL;
    double tolerance = 0.0;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }

    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  bytes = sqlite3_value_bytes (argv[0]);
	  geom1 =
	      gaiaFromSpatiaLiteBlobWkbEx (blob, bytes, gpkg_mode,
					   gpkg_amphibious);
      }
    else
	goto error;
    if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
      {
	  blob = (unsigned char *) sqlite3_value_blob (argv[1]);
	  bytes = sqlite3_value_bytes (argv[1]);
	  geom2 =
	      gaiaFromSpatiaLiteBlobWkbEx (blob, bytes, gpkg_mode,
					   gpkg_amphibious);
      }
    else
	goto error;
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[2]);
		tolerance = val;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[2]);
	  else
	      goto error;
      }

/* checking the arguments for validity */
    if (geom1 == NULL || geom2 == NULL)
	goto error;
    if (geom1->Srid != geom2->Srid)
	goto error;
    if (geom1->DimensionModel == GAIA_XY || geom1->DimensionModel == GAIA_XY_M)
	;
    else
	goto error;
    if (geom2->DimensionModel == GAIA_XY_Z
	|| geom2->DimensionModel == GAIA_XY_Z_M)
	;
    else
	goto error;
    if (!gaia_do_check_linestring (geom1))
	goto error;
    if (!gaia_do_check_linestring (geom2))
	goto error;
    if (tolerance < 0.0)
	goto error;

    geom3 = gaiaDrapeLine (db_handle, geom1, geom2, tolerance);
    if (geom3 == NULL)
	goto error;

    gaiaToSpatiaLiteBlobWkb (geom3, &blob, &bytes);
    sqlite3_result_blob (context, blob, bytes, free);
    gaiaFreeGeomColl (geom1);
    gaiaFreeGeomColl (geom2);
    gaiaFreeGeomColl (geom3);
    return;

  error:
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
    if (geom3 != NULL)
	gaiaFreeGeomColl (geom3);
    sqlite3_result_null (context);
}

static void
fnct_DrapeLineExceptions (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ ST_DrapeLineExceptions( line-1 Linestring, line-2 Linestring )
/    or
/ ST_DrapeLineExceptions( line-1 Linestring, line-2 Linestring, 
/                         tolerance  Double )
/    or
/ ST_DrapeLineExceptions( line-1 Linestring, line-2 Linestring, 
/                         tolerance  Double, interpolated Integer )
/
/ will drape line-1 over line-2 returning a MultiPoint containing
/ all Points from line-1 not correctly draped.
/ NULL on invalid arguments or if any error is encountered or
/ if all Point are correctly draped.
*/
    unsigned char *blob;
    int bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    gaiaGeomCollPtr geom3 = NULL;
    double tolerance = 0.0;
    int interpolated = 1;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }

    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (unsigned char *) sqlite3_value_blob (argv[0]);
	  bytes = sqlite3_value_bytes (argv[0]);
	  geom1 =
	      gaiaFromSpatiaLiteBlobWkbEx (blob, bytes, gpkg_mode,
					   gpkg_amphibious);
      }
    else
	goto error;
    if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
      {
	  blob = (unsigned char *) sqlite3_value_blob (argv[1]);
	  bytes = sqlite3_value_bytes (argv[1]);
	  geom2 =
	      gaiaFromSpatiaLiteBlobWkbEx (blob, bytes, gpkg_mode,
					   gpkg_amphibious);
      }
    else
	goto error;
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int val = sqlite3_value_int (argv[2]);
		tolerance = val;
	    }
	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      tolerance = sqlite3_value_double (argv[2]);
	  else
	      goto error;
      }
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	      interpolated = sqlite3_value_int (argv[3]);
	  else
	      goto error;
      }

/* checking the arguments for validity */
    if (geom1 == NULL || geom2 == NULL)
	goto error;
    if (geom1->Srid != geom2->Srid)
	goto error;
    if (geom1->DimensionModel == GAIA_XY || geom1->DimensionModel == GAIA_XY_M)
	;
    else
	goto error;
    if (geom2->DimensionModel == GAIA_XY_Z
	|| geom2->DimensionModel == GAIA_XY_Z_M)
	;
    else
	goto error;
    if (!gaia_do_check_linestring (geom1))
	goto error;
    if (!gaia_do_check_linestring (geom2))
	goto error;
    if (tolerance < 0.0)
	goto error;

    geom3 =
	gaiaDrapeLineExceptions (db_handle, geom1, geom2, tolerance,
				 interpolated);
    if (geom3 == NULL)
	goto error;

    gaiaToSpatiaLiteBlobWkb (geom3, &blob, &bytes);
    sqlite3_result_blob (context, blob, bytes, free);
    gaiaFreeGeomColl (geom1);
    gaiaFreeGeomColl (geom2);
    gaiaFreeGeomColl (geom3);
    return;

  error:
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
    if (geom3 != NULL)
	gaiaFreeGeomColl (geom3);
    sqlite3_result_null (context);
}

#endif /* end including GEOS */

static int
text2double (const unsigned char *str, double *val)
{
/* checks for a valid number, eventually returning a DOUBLE */
    int err = 0;
    int sign = 0;
    int decimal = 0;
    int exp = 0;
    int expsign = 0;
    const unsigned char *p = str;
    while (*p != '\0')
      {
	  switch (*p)
	    {
	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		break;
	    case '-':
	    case '+':
		if (!exp)
		    sign++;
		else
		    expsign++;
		break;
	    case '.':
		decimal++;
		break;
	    case 'e':
	    case 'E':
		exp++;
		break;
	    default:
		err = 1;
		break;
	    };
	  p++;
      }
    if (sign > 1 || expsign > 1 || decimal > 1 || (exp == 0 && expsign > 0))
	err = 1;
    if (err)
	return 0;
    *val = atof ((const char *) str);
    return 1;
}

static void
fnct_CastToInteger (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToInteger(generic value)
/
/ returns an INTEGER value [if conversion is possible] 
/ or NULL in any other case
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  sqlite3_int64 val = sqlite3_value_int64 (argv[0]);
	  sqlite3_result_int64 (context, val);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  sqlite3_int64 val;
	  double dval = sqlite3_value_double (argv[0]);
	  double diff = dval - floor (dval);
	  val = (sqlite3_int64) sqlite3_value_double (argv[0]);
	  if (diff >= 0.5)
	      val++;
	  sqlite3_result_int64 (context, val);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  const unsigned char *txt = sqlite3_value_text (argv[0]);
	  double dval;
	  if (text2double (txt, &dval))
	    {
		sqlite3_int64 val;
		double dval = sqlite3_value_double (argv[0]);
		double diff = dval - floor (dval);
		val = (sqlite3_int64) sqlite3_value_double (argv[0]);
		if (diff >= 0.5)
		    val++;
		sqlite3_result_int64 (context, val);
		return;
	    }
      }
    sqlite3_result_null (context);
}

static void
fnct_CastToDouble (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToDouble(generic value)
/
/ returns a DOUBLE value [if conversion is possible] 
/ or NULL in any other case
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  double val = (double) sqlite3_value_int64 (argv[0]);
	  sqlite3_result_double (context, val);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  double val = sqlite3_value_double (argv[0]);
	  sqlite3_result_double (context, val);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  const unsigned char *txt = sqlite3_value_text (argv[0]);
	  double val;
	  if (text2double (txt, &val))
	    {
		sqlite3_result_double (context, val);
		return;
	    }
      }
    sqlite3_result_null (context);
}

static void
fnct_CastToText (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToText(generic value)
/ CastToText(generic value, Integer left-aligned-length)
/
/ returns a TEXT value [if conversion is possible] 
/ or NULL in any other case
*/
    char *txt;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  char format[32];
	  const char *fmt = FRMT64;
	  sqlite3_int64 val;
	  if (argc == 2)
	    {
		int length;
		if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		length = sqlite3_value_int (argv[1]);
		if (length > 0)
		  {
		      sprintf (format, "%%0%d" FRMT64_WO_PCT, length);
		      fmt = format;
		  }
	    }
	  val = sqlite3_value_int64 (argv[0]);
	  txt = sqlite3_mprintf (fmt, val);
	  sqlite3_result_text (context, txt, strlen (txt), sqlite3_free);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  int i;
	  int len;
	  double val = sqlite3_value_double (argv[0]);
	  char format[32];
	  const char *fmt = "%1.18f";
	  if (argc == 2)
	    {
		int length;
		if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		length = sqlite3_value_int (argv[1]);
		if (length > 0)
		  {
		      sprintf (format, "%%0%d.18f", length + 19);
		      fmt = format;
		  }
	    }
	  txt = sqlite3_mprintf (fmt, val);
	  len = strlen (txt);
	  for (i = len - 1; i > 0; i--)
	    {
		/* suppressing meaningless trailing zeroes */
		if (txt[i] >= '1' && txt[i] <= '9')
		    break;
		if (txt[i] == '.')
		  {
		      txt[i + 1] = '0';
		      break;
		  }
		if (txt[i] == '0')
		    txt[i] = '\0';
	    }
	  sqlite3_result_text (context, txt, strlen (txt), sqlite3_free);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  int n_bytes;
	  txt = (char *) sqlite3_value_text (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
	  sqlite3_result_text (context, txt, n_bytes, SQLITE_TRANSIENT);
	  return;
      }
    sqlite3_result_null (context);
}

static int
parseHexByte (unsigned char hi, unsigned char lo, unsigned char *val)
{
/* converting a byte for its Hex representation */
    unsigned char x;
    switch (hi)
      {
      case '0':
	  x = 0;
	  break;
      case '1':
	  x = 16;
	  break;
      case '2':
	  x = 16 * 2;
	  break;
      case '3':
	  x = 16 * 3;
	  break;
      case '4':
	  x = 16 * 4;
	  break;
      case '5':
	  x = 16 * 5;
	  break;
      case '6':
	  x = 16 * 6;
	  break;
      case '7':
	  x = 16 * 7;
	  break;
      case '8':
	  x = 16 * 8;
	  break;
      case '9':
	  x = 16 * 9;
	  break;
      case 'a':
      case 'A':
	  x = 16 * 10;
	  break;
      case 'b':
      case 'B':
	  x = 16 * 11;
	  break;
      case 'c':
      case 'C':
	  x = 16 * 12;
	  break;
      case 'd':
      case 'D':
	  x = 16 * 13;
	  break;
      case 'e':
      case 'E':
	  x = 16 * 14;
	  break;
      case 'f':
      case 'F':
	  x = 16 * 15;
	  break;
      default:
	  return 0;
      };
    switch (lo)
      {
      case '0':
	  x += 0;
	  break;
      case '1':
	  x += 1;
	  break;
      case '2':
	  x += 2;
	  break;
      case '3':
	  x += 3;
	  break;
      case '4':
	  x += 4;
	  break;
      case '5':
	  x += 5;
	  break;
      case '6':
	  x += 6;
	  break;
      case '7':
	  x += 7;
	  break;
      case '8':
	  x += 8;
	  break;
      case '9':
	  x += 9;
	  break;
      case 'a':
      case 'A':
	  x += 10;
	  break;
      case 'b':
      case 'B':
	  x += 11;
	  break;
      case 'c':
      case 'C':
	  x += 12;
	  break;
      case 'd':
      case 'D':
	  x += 13;
	  break;
      case 'e':
      case 'E':
	  x += 14;
	  break;
      case 'f':
      case 'F':
	  x += 15;
	  break;
      default:
	  return 0;
      };
    *val = x;
    return 1;
}

static int
parseHexString (const unsigned char *in, int in_len, unsigned char **out,
		int *out_len)
{
/* parsing an Hexadecimal string */
    unsigned char *buf;
    unsigned char *p_out;
    unsigned char byteval;
    int i;
    int len;
    *out = NULL;
    *out_len = 0;
    if (in == NULL)
	return 0;
    len = in_len / 2;
    if (len * 2 != in_len)	/* # digits is an odd number */
	return 0;
    buf = malloc (len);
    p_out = buf;
    for (i = 0; i < in_len; i += 2)
      {
	  if (!parseHexByte (in[i], in[i + 1], &byteval))
	      goto error;
	  *p_out++ = byteval;
      }
    *out = buf;
    *out_len = len;
    return 1;
  error:
    free (buf);
    return 0;
}

static void
fnct_CastToBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CastToBlob(generic value)
/   or
/ CastToBlob(generic value, boolen hex_input)
/
/ returns a BLOB value [if conversion is possible] 
/ or NULL in any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    int is_hex = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  is_hex = sqlite3_value_int (argv[1]);
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  p_blob = sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
	  if (is_hex)
	    {
		/* attempting to convert Hexadecimal input */
		unsigned char *blob;
		int bytes;
		if (!parseHexString (p_blob, n_bytes, &blob, &bytes))
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		sqlite3_result_blob (context, blob, bytes, free);
		return;
	    }
	  sqlite3_result_blob (context, p_blob, n_bytes, SQLITE_TRANSIENT);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  p_blob = sqlite3_value_text (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
	  if (is_hex)
	    {
		/* attempting to convert Hexadecimal input */
		unsigned char *blob;
		int bytes;
		if (!parseHexString (p_blob, n_bytes, &blob, &bytes))
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		sqlite3_result_blob (context, blob, bytes, free);
		return;
	    }
	  sqlite3_result_blob (context, p_blob, n_bytes, SQLITE_TRANSIENT);
	  return;
      }
    sqlite3_result_null (context);
}

static void
fnct_ForceAsNull (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ForceAsNull(generic val1, generic val2)
/
/ returns a NULL value if val1 and val2 are equal 
/ (and exactly of the same type)
/ return val1 in any other case
*/
    int type1;
    int type2;
    const unsigned char *p_blob;
    int n_bytes;
    const unsigned char *p_blob2;
    int n_bytes2;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    type1 = sqlite3_value_type (argv[0]);
    type2 = sqlite3_value_type (argv[1]);
    if (type1 == type2)
      {
	  switch (type1)
	    {
	    case SQLITE_INTEGER:
		if (sqlite3_value_int64 (argv[0]) ==
		    sqlite3_value_int64 (argv[1]))
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		break;
	    case SQLITE_FLOAT:
		if (sqlite3_value_double (argv[0]) ==
		    sqlite3_value_double (argv[1]))
		  {
		      sqlite3_result_null (context);
		      return;
		  }
		break;
	    case SQLITE_TEXT:
		p_blob = sqlite3_value_text (argv[0]);
		n_bytes = sqlite3_value_bytes (argv[0]);
		p_blob2 = sqlite3_value_text (argv[1]);
		n_bytes2 = sqlite3_value_bytes (argv[1]);
		if (n_bytes == n_bytes2)
		  {
		      if (strcasecmp
			  ((const char *) p_blob, (const char *) p_blob2) == 0)
			{
			    sqlite3_result_null (context);
			    return;
			}
		  }
		break;
	    case SQLITE_BLOB:
		p_blob = sqlite3_value_blob (argv[0]);
		n_bytes = sqlite3_value_bytes (argv[0]);
		p_blob2 = sqlite3_value_blob (argv[1]);
		n_bytes2 = sqlite3_value_bytes (argv[1]);
		if (n_bytes == n_bytes2)
		  {
		      if (memcmp (p_blob, p_blob2, n_bytes) == 0)
			{
			    sqlite3_result_null (context);
			    return;
			}
		  }
		break;
	    case SQLITE_NULL:
		sqlite3_result_null (context);
		return;
	    };
      }
/* returning the first argument */
    switch (type1)
      {
      case SQLITE_INTEGER:
	  sqlite3_result_int64 (context, sqlite3_value_int64 (argv[0]));
	  break;
      case SQLITE_FLOAT:
	  sqlite3_result_double (context, sqlite3_value_double (argv[0]));
	  break;
      case SQLITE_TEXT:
	  p_blob = sqlite3_value_text (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
	  sqlite3_result_text (context, (const char *) p_blob, n_bytes,
			       SQLITE_TRANSIENT);
	  break;
      case SQLITE_BLOB:
	  p_blob = sqlite3_value_blob (argv[0]);
	  n_bytes = sqlite3_value_bytes (argv[0]);
	  sqlite3_result_blob (context, p_blob, n_bytes, SQLITE_TRANSIENT);
	  break;
      default:
	  sqlite3_result_null (context);
	  break;
      };
}

static void
fnct_CreateUUID (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CreateUUID()
/
/ returns a TEXT value containing an UUID
/ [xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx]
*/
    unsigned char rnd[16];
    char uuid[64];
    char *p = uuid;
    int i;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_randomness (16, rnd);
    for (i = 0; i < 16; i++)
      {
	  if (i == 4 || i == 6 || i == 8 || i == 10)
	      *p++ = '-';
	  sprintf (p, "%02x", rnd[i]);
	  p += 2;
      }
    *p = '\0';
    uuid[14] = '4';
    uuid[19] = '8';
    sqlite3_result_text (context, uuid, strlen (uuid), SQLITE_TRANSIENT);
}

static void
fnct_MD5Checksum (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ MD5Checksum(blob)
/
/ returns a TEXT value containing an hex MD5 checksum
/ [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
/      or
/ NULL on invalid arguments
*/
    void *md5;
    char *checksum;
    const unsigned char *blob;
    int blob_len;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = sqlite3_value_blob (argv[0]);
	  blob_len = sqlite3_value_bytes (argv[0]);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  blob = sqlite3_value_text (argv[0]);
	  blob_len = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
/* creating an MD5 object */
    md5 = gaiaCreateMD5Checksum ();
/* evaluating the BLOB */
    gaiaUpdateMD5Checksum (md5, blob, blob_len);
    checksum = gaiaFinalizeMD5Checksum (md5);
    gaiaFreeMD5Checksum (md5);
    if (checksum == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, checksum, strlen (checksum), free);
}

static void
fnct_MD5TotalChecksum_step (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ MD5TotalChecksum(BLOB)
/
/ aggregate function - STEP
/
*/
    void **p;
    void *md5;
    const unsigned char *blob;
    int blob_len;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = sqlite3_value_blob (argv[0]);
	  blob_len = sqlite3_value_bytes (argv[0]);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
      {
	  blob = sqlite3_value_text (argv[0]);
	  blob_len = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p = sqlite3_aggregate_context (context, sizeof (void *));
    if (!(*p))
      {
	  /* this is the first row - creating an MD5 object */
	  md5 = gaiaCreateMD5Checksum ();
	  gaiaUpdateMD5Checksum (md5, blob, blob_len);
	  *p = md5;
      }
    else
      {
	  /* subsequent rows */
	  md5 = *p;
	  gaiaUpdateMD5Checksum (md5, blob, blob_len);
      }
}

static void
fnct_MD5TotalChecksum_final (sqlite3_context * context)
{
/* SQL function:
/ MD5TotalChecksum(BLOB)
/
/ aggregate function - FINAL
/
*/
    void **p;
    void *md5;
    char *checksum;
    p = sqlite3_aggregate_context (context, 0);
    if (!(*p))
      {
	  sqlite3_result_null (context);
	  return;
      }
    md5 = *p;
    checksum = gaiaFinalizeMD5Checksum (md5);
    gaiaFreeMD5Checksum (md5);
    if (checksum == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, checksum, strlen (checksum), free);
}

#if OMIT_ICONV == 0		/* ICONV is absolutely required */

static void
fnct_EncodeURL (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ EncodeURL(text url)
/   or
/ EncodeURL(text url, text out_charset)
/
/ returns a TEXT value containing the percent-encoded URL
/      or
/ NULL on invalid arguments
*/
    const char *url;
    const char *out_charset = "UTF-8";
    char *encoded;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	url = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 1)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	      out_charset = (const char *) sqlite3_value_text (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
/* encoding the URL */
    encoded = gaiaEncodeURL (url, out_charset);
    if (encoded == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, encoded, strlen (encoded), free);
}

static void
fnct_DecodeURL (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ DecodeURL(text)
/   or
/ DecodeURL(text url, text in_charset)
/
/ returns a TEXT value containing the URL cleaned from percent-encoding
/      or
/ NULL on invalid arguments
*/
    char *url;
    const char *encoded;
    const char *in_charset = "UTF-8";
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	encoded = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 1)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	      in_charset = (const char *) sqlite3_value_text (argv[1]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
/* decoding the URL */
    url = gaiaDecodeURL (encoded, in_charset);
    if (url == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, url, strlen (url), free);
}

#endif /* ICONV enabled/disabled */

static void
fnct_DirNameFromPath (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ DirNameFromPath(text)
/
/ returns a TEXT value containing the Directory Name from a Path
/      or
/ NULL on invalid arguments
*/
    char *dir;
    const char *path;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
/* breaking the Path */
    dir = gaiaDirNameFromPath (path);
    if (dir == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, dir, strlen (dir), free);
}

static void
fnct_FullFileNameFromPath (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ FullFileNameFromPath(text)
/
/ returns a TEXT value containing the Full FileName (including extension)
/ from a Path
/      or
/ NULL on invalid arguments
*/
    char *name;
    const char *path;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
/* breaking the Path */
    name = gaiaFullFileNameFromPath (path);
    if (name == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, name, strlen (name), free);
}

static void
fnct_FileNameFromPath (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ FileNameFromPath(text)
/
/ returns a TEXT value containing the FileName (excluding extension)
/ from a Path
/      or
/ NULL on invalid arguments
*/
    char *name;
    const char *path;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
/* breaking the Path */
    name = gaiaFileNameFromPath (path);
    if (name == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, name, strlen (name), free);
}

static void
fnct_FileExtFromPath (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ FileExtFromPath(text)
/
/ returns a TEXT value containing the Extension (if any) from a Path
/      or
/ NULL on invalid arguments
*/
    char *ext;
    const char *path;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
/* breaking the Path */
    ext = gaiaFileExtFromPath (path);
    if (ext == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, ext, strlen (ext), free);
}

static void
fnct_make_string_list_step (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ MakeStringList(X)
/ MakeStringList(X, separator)
/
/ aggregate function - STEP
/
*/
    struct string_list_str *p;
    char buf[1024];
    const char *str = buf;
    char separator = ',';
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	str = (const char *) sqlite3_value_text (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
#if defined(_WIN32) && !defined(__MINGW32__)
	sprintf (buf, "%I64d", sqlite3_value_int64 (argv[0]));
#else
	sprintf (buf, "%lld", sqlite3_value_int64 (argv[0]));
#endif
    else
	strcpy (buf, "ILLEGAL_VALUE");
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	    {
		const char *sep = (const char *)sqlite3_value_text (argv[1]);
		separator = *sep;
	    }
	  else
	      return;
      }
    p = sqlite3_aggregate_context (context, sizeof (struct string_list_str));
    if (p->separator == '\0' && p->string == NULL)
      {
	  /* first item */
	  p->separator = separator;
	  if (separator == '\0')
	      separator = ',';
	  p->string = sqlite3_mprintf ("%s", str);
      }
    else
      {
	  char *oldstr = p->string;
	  p->string = sqlite3_mprintf ("%s%c%s", oldstr, p->separator, str);
	  sqlite3_free (oldstr);
      }
}

static void
fnct_make_string_list_final (sqlite3_context * context)
{
/* SQL function:
/ MakeStringList(X)
/ MakeStringList(X, separator)
/ aggregate function -  FINAL
/
*/
    struct string_list_str *p = sqlite3_aggregate_context (context, 0);
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_text (context, p->string, -1, sqlite3_free);
}


#ifndef OMIT_MATHSQL		/* supporting SQL math functions */

static int
testInvalidFP (double x)
{
/* testing if this one is an invalid Floating Point */
#ifdef _WIN32
    if (_fpclass (x) == _FPCLASS_NN || _fpclass (x) == _FPCLASS_PN ||
	_fpclass (x) == _FPCLASS_NZ || _fpclass (x) == _FPCLASS_PZ)
	;
    else
	return 1;
#else
    if (fpclassify (x) == FP_NORMAL || fpclassify (x) == FP_ZERO)
	;
    else
	return 1;
#endif
    return 0;
}

static void
fnct_math_acos (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ acos(double X)
/
/ Returns the arc cosine of X, that is, the value whose cosine is X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = acos (sqlite3_value_double (argv[0]));
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = acos (x);
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_asin (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ asin(double X)
/
/ Returns the arc sine of X, that is, the value whose sine is X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = asin (sqlite3_value_double (argv[0]));
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = asin (x);
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_atan (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ atan(double X)
/
/ Returns the arc tangent of X, that is, the value whose tangent is X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = atan (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = atan (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_atan2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ atan2(double Y, double X)
/
/ Returns  the principal value of the arc tangent of Y/X, using
/ the signs of the two arguments to determine the quadrant of 
/ the result.
/ or NULL if any error is encountered
*/
    int int_value;
    double x = 0.0;
    double y = 0.0;
    double t;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    t = atan2 (y, x);
    sqlite3_result_double (context, t);
}

static void
fnct_math_ceil (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ceil(double X)
/
/ Returns the smallest integer value not less than X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = ceil (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = ceil (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_cos (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ cos(double X)
/
/ Returns the cosine of X, where X is given in radians
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = cos (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = cos (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_cot (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ cot(double X)
/
/ Returns the cotangent of X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    double tang;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    tang = tan (x);
    if (tang == 0.0)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = 1.0 / tang;
    sqlite3_result_double (context, x);
}

static void
fnct_math_degrees (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ degrees(double X)
/
/ Returns the argument X, converted from radians to degrees
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = x * 57.29577951308232;
    sqlite3_result_double (context, x);
}

static void
fnct_math_exp (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ exp(double X)
/
/ Returns the value of e (the base of natural logarithms) raised to the power of X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = exp (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = exp (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_floor (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ floor(double X)
/
/ Returns the largest integer value not greater than X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = floor (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = floor (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_logn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ log(double X)
/
/ Returns the natural logarithm of X; that is, the base-e logarithm of X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = log (sqlite3_value_double (argv[0]));
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = log (x);
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_logn2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ log(double X, double B)
/
/ Returns the logarithm of X to the base B
/ or NULL if any error is encountered
*/
    int int_value;
    double x = 0.0;
    double b = 1.0;
    double log1;
    double log2;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	b = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  b = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (x <= 0.0 || b <= 1.0)
      {
	  sqlite3_result_null (context);
	  return;
      }
    log1 = log (x);
    if (testInvalidFP (log1))
      {
	  sqlite3_result_null (context);
	  return;
      }
    log2 = log (b);
    if (testInvalidFP (log2))
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_double (context, log1 / log2);
}

static void
fnct_math_log_2 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ log2(double X)
/
/ Returns the base-2 logarithm of X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    double log1;
    double log2;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    log1 = log (x);
    if (testInvalidFP (log1))
      {
	  sqlite3_result_null (context);
	  return;
      }
    log2 = log (2.0);
    sqlite3_result_double (context, log1 / log2);
}

static void
fnct_math_log_10 (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ log10(double X)
/
/ Returns the base-10 logarithm of X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    double log1;
    double log2;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    log1 = log (x);
    if (testInvalidFP (log1))
      {
	  sqlite3_result_null (context);
	  return;
      }
    log2 = log (10.0);
    sqlite3_result_double (context, log1 / log2);
}

static void
fnct_math_pi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ pi(void)
/
/ Returns the value of (pi)
*/
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    sqlite3_result_double (context, 3.14159265358979323846);
}

static void
fnct_math_pow (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ pow(double X, double Y)
/
/ Returns the value of X raised to the power of Y.
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    double y;
    double p;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	y = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  y = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p = pow (x, y);
    if (testInvalidFP (p))
	sqlite3_result_null (context);
    else
	sqlite3_result_double (context, p);
}

static void
fnct_math_stddev_step (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ stddev_pop(double X)
/ stddev_samp(double X)
/ var_pop(double X)
/ var_samp(double X)
/
/ aggregate function - STEP
/
*/
    struct stddev_str *p;
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
	return;
    p = sqlite3_aggregate_context (context, sizeof (struct stddev_str));
    if (!(p->cleaned))
      {
	  p->cleaned = 1;
	  p->mean = x;
	  p->quot = 0.0;
	  p->count = 0.0;
      }
    p->count += 1.0;
    p->quot =
	p->quot +
	(((p->count - 1.0) * ((x - p->mean) * (x - p->mean))) / p->count);
    p->mean = p->mean + ((x - p->mean) / p->count);
}

static void
fnct_math_stddev_pop_final (sqlite3_context * context)
{
/* SQL function:
/ stddev_pop(double X)
/ aggregate function -  FINAL
/
*/
    double x;
    struct stddev_str *p = sqlite3_aggregate_context (context, 0);
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = sqrt (p->quot / p->count);
    sqlite3_result_double (context, x);
}

static void
fnct_math_stddev_samp_final (sqlite3_context * context)
{
/* SQL function:
/ stddev_samp(double X)
/ aggregate function -  FINAL
/
*/
    double x;
    struct stddev_str *p = sqlite3_aggregate_context (context, 0);
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = sqrt (p->quot / (p->count - 1.0));
    sqlite3_result_double (context, x);
}

static void
fnct_math_var_pop_final (sqlite3_context * context)
{
/* SQL function:
/ var_pop(double X)
/ aggregate function -  FINAL
/
*/
    double x;
    struct stddev_str *p = sqlite3_aggregate_context (context, 0);
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = p->quot / p->count;
    sqlite3_result_double (context, x);
}

static void
fnct_math_var_samp_final (sqlite3_context * context)
{
/* SQL function:
/ var_samp(double X)
/ aggregate function -  FINAL
/
*/
    double x;
    struct stddev_str *p = sqlite3_aggregate_context (context, 0);
    if (!p)
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = p->quot / (p->count - 1.0);
    sqlite3_result_double (context, x);
}

static void
fnct_math_radians (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ radians(double X)
/
/ Returns the argument X, converted from degrees to radians
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    x = x * .0174532925199432958;
    sqlite3_result_double (context, x);
}

static void
fnct_math_sign (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ sign(double X)
/
/ Returns the sign of the argument as -1, 0, or 1, depending on whether X is negative, zero, or positive
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	x = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (x > 0.0)
	sqlite3_result_double (context, 1.0);
    else if (x < 0.0)
	sqlite3_result_double (context, -1.0);
    else
	sqlite3_result_double (context, 0.0);
}

static void
fnct_math_sin (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ sin(double X)
/
/ Returns the sine of X, where X is given in radians
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = sin (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = sin (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_sqrt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ sqrt(double X)
/
/ Returns the square root of a non-negative number X
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = sqrt (sqlite3_value_double (argv[0]));
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = sqrt (x);
	  if (testInvalidFP (x))
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_math_tan (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ tan(double X)
/
/ Returns the tangent of X, where X is given in radians
/ or NULL if any error is encountered
*/
    int int_value;
    double x;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
      {
	  x = tan (sqlite3_value_double (argv[0]));
	  sqlite3_result_double (context, x);
      }
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  x = int_value;
	  x = tan (x);
	  sqlite3_result_double (context, x);
      }
    else
	sqlite3_result_null (context);
}

#endif /* end supporting SQL math functions */

static void
fnct_GeomFromExifGpsBlob (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ GeomFromExifGpsBlob(BLOB encoded image)
/
/ returns:
/ a POINT geometry
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom;
    unsigned char *geoblob;
    int geosize;
    double longitude;
    double latitude;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (gaiaGetGpsCoords (p_blob, n_bytes, &longitude, &latitude))
      {
	  geom = gaiaAllocGeomColl ();
	  geom->Srid = 4326;
	  gaiaAddPointToGeomColl (geom, longitude, latitude);
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &geoblob, &geosize, gpkg_mode,
				      tiny_point);
	  gaiaFreeGeomColl (geom);
	  sqlite3_result_blob (context, geoblob, geosize, free);
      }
    else
	sqlite3_result_null (context);
}

static char *
guess_mime_type (const unsigned char *p_blob, int n_bytes)
{
/* guessing the mime-type corresponding to some BLOB */
    int blob_type;
    const char *mime = NULL;
    int len;
    char *string = NULL;
    blob_type = gaiaGuessBlobType (p_blob, n_bytes);
    switch (blob_type)
      {
      case GAIA_ZIP_BLOB:
	  mime = "application/zip";
	  break;
      case GAIA_PDF_BLOB:
	  mime = "application/pdf";
	  break;
      case GAIA_TIFF_BLOB:
	  mime = "image/tiff";
	  break;
      case GAIA_GIF_BLOB:
	  mime = "image/gif";
	  break;
      case GAIA_PNG_BLOB:
	  mime = "image/png";
	  break;
      case GAIA_JP2_BLOB:
	  mime = "image/jp2";
	  break;
      case GAIA_JPEG_BLOB:
      case GAIA_EXIF_BLOB:
      case GAIA_EXIF_GPS_BLOB:
	  mime = "image/jpeg";
	  break;
#ifdef ENABLE_LIBXML2		/* including LIBXML2 */
      case GAIA_XML_BLOB:
	  mime = "application/xml";
	  if (gaiaIsSvgXmlBlob (p_blob, n_bytes))
	      mime = "image/svg+xml";
	  break;
#endif /* end including LIBXML2 */
      };
    if (mime != NULL)
      {
	  len = strlen (mime);
	  string = malloc (len + 1);
	  strcpy (string, mime);
      }
    return string;
}

static void
fnct_GetMimeType (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GetMimeType(BLOB)
/
/ returns:
/ the Mime-Type corresponding to the BLOB
/ or NULL if any error is encountered or no valid mime is defined
*/
    unsigned char *p_blob;
    int n_bytes;
    char *mime = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    mime = guess_mime_type (p_blob, n_bytes);
    if (mime == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, mime, strlen (mime), free);
}

static void
blob_guess (sqlite3_context * context, int argc, sqlite3_value ** argv,
	    int request)
{
/* SQL function:
/ IsGifBlob(BLOB encoded image)
/ IsPngBlob, IsJpegBlob, IsExifBlob, IsExifGpsBlob, IsTiffBlob,
/ IsZipBlob, IsPdfBlob, IsJP2Blob, IsGeometryBlob, IsTinyPointBlob
/
/ returns:
/ 1 if the required BLOB_TYPE is TRUE
/ 0 otherwise
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int blob_type;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    blob_type = gaiaGuessBlobType (p_blob, n_bytes);
    if (request == GAIA_GEOMETRY_BLOB)
      {
	  if (blob_type == GAIA_GEOMETRY_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_TINYPOINT_BLOB)
      {
	  if (blob_type == GAIA_TINYPOINT_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_ZIP_BLOB)
      {
	  if (blob_type == GAIA_ZIP_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_PDF_BLOB)
      {
	  if (blob_type == GAIA_PDF_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_TIFF_BLOB)
      {
	  if (blob_type == GAIA_TIFF_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_GIF_BLOB)
      {
	  if (blob_type == GAIA_GIF_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_PNG_BLOB)
      {
	  if (blob_type == GAIA_PNG_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_JPEG_BLOB)
      {
	  if (blob_type == GAIA_JPEG_BLOB || blob_type == GAIA_EXIF_BLOB
	      || blob_type == GAIA_EXIF_GPS_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_EXIF_BLOB)
      {
	  if (blob_type == GAIA_EXIF_BLOB || blob_type == GAIA_EXIF_GPS_BLOB)
	    {
		sqlite3_result_int (context, 1);
	    }
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_EXIF_GPS_BLOB)
      {
	  if (blob_type == GAIA_EXIF_GPS_BLOB)
	    {
		sqlite3_result_int (context, 1);
	    }
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_WEBP_BLOB)
      {
	  if (blob_type == GAIA_WEBP_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    if (request == GAIA_JP2_BLOB)
      {
	  if (blob_type == GAIA_JP2_BLOB)
	      sqlite3_result_int (context, 1);
	  else
	      sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_result_int (context, -1);
}

/*
/ the following functions simply readdress the blob_guess()
/ setting the appropriate request mode
*/

static void
fnct_IsGeometryBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_GEOMETRY_BLOB);
}

static void
fnct_IsTinyPointBlob (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_TINYPOINT_BLOB);
}

static void
fnct_IsZipBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_ZIP_BLOB);
}

static void
fnct_IsPdfBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_PDF_BLOB);
}

static void
fnct_IsTiffBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_TIFF_BLOB);
}

static void
fnct_IsGifBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_GIF_BLOB);
}

static void
fnct_IsPngBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_PNG_BLOB);
}

static void
fnct_IsJpegBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_JPEG_BLOB);
}

static void
fnct_IsExifBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_EXIF_BLOB);
}

static void
fnct_IsExifGpsBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_EXIF_GPS_BLOB);
}

static void
fnct_IsWebPBlob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_WEBP_BLOB);
}

static void
fnct_IsJP2Blob (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    blob_guess (context, argc, argv, GAIA_JP2_BLOB);
}

static void
fnct_BlobFromFile (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BlobFromFile(TEXT filepath)
/
/ returns:
/ some BLOB on success
/ or NULL on failure
*/
    unsigned char *p_blob;
    int n_bytes;
    int max_blob;
    int rd;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    const char *path = NULL;
    FILE *in = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[0]);
    if (path == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    in = fopen (path, "rb");
    if (in == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    else
      {
	  /* querying the file length */
	  if (fseek (in, 0, SEEK_END) < 0)
	    {
		sqlite3_result_null (context);
		fclose (in);
		return;
	    }
	  n_bytes = ftell (in);
	  max_blob = sqlite3_limit (sqlite, SQLITE_LIMIT_LENGTH, -1);
	  if (n_bytes > max_blob)
	    {
		/* too big; cannot be stored into a BLOB */
		sqlite3_result_null (context);
		fclose (in);
		return;
	    }
	  rewind (in);
	  p_blob = malloc (n_bytes);
	  /* attempting to load the BLOB from the file */
	  rd = fread (p_blob, 1, n_bytes, in);
	  fclose (in);
	  if (rd != n_bytes)
	    {
		/* read error */
		free (p_blob);
		sqlite3_result_null (context);
		return;
	    }
	  sqlite3_result_blob (context, p_blob, n_bytes, free);
      }
}

static void
fnct_BlobToFile (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ BlobToFile(BLOB payload, TEXT filepath)
/
/ returns:
/ 1 on success
/ or 0 on failure
*/
    unsigned char *p_blob;
    int n_bytes;
    const char *path = NULL;
    FILE *out = NULL;
    int ret = 1;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	path = (const char *) sqlite3_value_text (argv[1]);
    if (path == NULL)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    out = fopen (path, "wb");
    if (out == NULL)
	ret = 0;
    else
      {
	  /* exporting the BLOB into the file */
	  int wr = fwrite (p_blob, 1, n_bytes, out);
	  if (wr != n_bytes)
	      ret = 0;
	  fclose (out);
      }
    sqlite3_result_int (context, ret);
}

#ifndef OMIT_GEOS		/* only if GEOS is enabled */

static int
load_dxf (sqlite3 * db_handle, struct splite_internal_cache *cache,
	  char *filename, int srid, int append, int force_dims, int mode,
	  int special_rings, char *prefix, char *layer_name)
{
/* scanning a Directory and processing all DXF files */
    int ret;
    gaiaDxfParserPtr dxf = NULL;

/* creating a DXF parser */
    dxf = gaiaCreateDxfParser (srid, force_dims, prefix, layer_name,
			       special_rings);
    if (dxf == NULL)
      {
	  ret = 0;
	  goto stop_dxf;
      }
/* attempting to parse the DXF input file */
    if (gaiaParseDxfFile_r (cache, dxf, filename))
      {
	  /* loading into the DB */
	  if (!gaiaLoadFromDxfParser (db_handle, dxf, mode, append))
	    {
		ret = 0;
		spatialite_e ("DB error while loading: %s\n", filename);
	    }
      }
    else
      {
	  ret = 0;
	  spatialite_e ("Unable to parse: %s\n", filename);
	  goto stop_dxf;
      }
    spatialite_e ("\n*** DXF file successfully loaded\n");
    ret = 1;

  stop_dxf:
    /* destroying the DXF parser */
    gaiaDestroyDxfParser (dxf);
    return ret;
}

static void
fnct_ImportDXF (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ImportDXF(TEXT filename)
/     or
/ InportDXF(TEXT filename, INT srid, INT append, TEXT dims,
/           TEXT mode, TEXT special_rings, TEXT table_prefix,
/           TEXT layer_name)
/
/ returns:
/ 1 on success
/ or 0 on failure
/ NULL on invalid arguments
*/
    int ret;
    char *filename;
    int srid = -1;
    int append = 0;
    int special_rings = GAIA_DXF_RING_NONE;
    int mode = GAIA_DXF_IMPORT_BY_LAYER;
    int force_dims = GAIA_DXF_AUTO_2D_3D;
    char *prefix = NULL;
    char *layer_name = NULL;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    filename = (char *) sqlite3_value_text (argv[0]);
    if (argc > 7)
      {
	  const char *value;
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  srid = sqlite3_value_int (argv[1]);
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  append = sqlite3_value_int (argv[2]);
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[3]);
	  if (strcasecmp (value, "2D") == 0)
	      force_dims = GAIA_DXF_FORCE_2D;
	  else if (strcasecmp (value, "3D") == 0)
	      force_dims = GAIA_DXF_FORCE_3D;
	  else if (strcasecmp (value, "AUTO") == 0)
	      force_dims = GAIA_DXF_AUTO_2D_3D;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[4]);
	  if (strcasecmp (value, "MIXED") == 0)
	      mode = GAIA_DXF_IMPORT_MIXED;
	  else if (strcasecmp (value, "DISTINCT") == 0)
	      mode = GAIA_DXF_IMPORT_BY_LAYER;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[5]);
	  if (strcasecmp (value, "LINKED") == 0)
	      special_rings = GAIA_DXF_RING_LINKED;
	  else if (strcasecmp (value, "UNLINKED") == 0)
	      special_rings = GAIA_DXF_RING_UNLINKED;
	  else if (strcasecmp (value, "NONE") == 0)
	      special_rings = GAIA_DXF_RING_NONE;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[6]) == SQLITE_TEXT)
	      prefix = (char *) sqlite3_value_text (argv[6]);
	  else if (sqlite3_value_type (argv[6]) != SQLITE_NULL)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[7]) == SQLITE_TEXT)
	      layer_name = (char *) sqlite3_value_text (argv[7]);
	  else if (sqlite3_value_type (argv[7]) != SQLITE_NULL)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    ret =
	load_dxf (db_handle, cache, filename, srid, append, force_dims, mode,
		  special_rings, prefix, layer_name);
    sqlite3_result_int (context, ret);
}

static int
is_dxf_file (const char *filename)
{
/* testing if a FileName ends with the expected suffix */
    int len = strlen (filename);
    int off = len - 4;
    if (off >= 1)
      {
	  if (strcasecmp (filename + off, ".dxf") == 0)
	      return 1;
      }
    return 0;
}

static int
scan_dxf_dir (sqlite3 * db_handle, struct splite_internal_cache *cache,
	      char *dir_path, int srid, int append, int force_dims, int mode,
	      int special_rings, char *prefix, char *layer_name)
{
/* scanning a Directory and processing all DXF files */
    int cnt = 0;
    char *filepath;
#if defined(_WIN32) && !defined(__MINGW32__)
/* Visual Studio .NET */
    struct _finddata_t c_file;
    intptr_t hFile;
    if (_chdir (dir_path) < 0)
	return 0;
    if ((hFile = _findfirst ("*.*", &c_file)) == -1L)
	;
    else
      {
	  while (1)
	    {
		if ((c_file.attrib & _A_RDONLY) == _A_RDONLY
		    || (c_file.attrib & _A_NORMAL) == _A_NORMAL)
		  {
		      if (is_dxf_file (c_file.name))
			{
			    filepath =
				sqlite3_mprintf ("%s/%s", dir_path,
						 c_file.name);
			    cnt +=
				load_dxf (db_handle, cache, filepath, srid,
					  append, force_dims, mode,
					  special_rings, prefix, layer_name);
			    sqlite3_free (filepath);
			}
		  }
		if (_findnext (hFile, &c_file) != 0)
		    break;
	    };
	  _findclose (hFile);
      }
#else
/* not Visual Studio .NET */
    struct dirent *entry;
    DIR *dir = opendir (dir_path);
    if (!dir)
	return 0;
    while (1)
      {
	  /* scanning dir-entries */
	  entry = readdir (dir);
	  if (!entry)
	      break;
	  if (is_dxf_file (entry->d_name))
	    {
		filepath = sqlite3_mprintf ("%s/%s", dir_path, entry->d_name);
		cnt +=
		    load_dxf (db_handle, cache, filepath, srid, append,
			      force_dims, mode, special_rings, prefix,
			      layer_name);
		sqlite3_free (filepath);
	    }
      }
    closedir (dir);
#endif
    return cnt;
}

static void
fnct_ImportDXFfromDir (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ ImportDXFfromDir(TEXT dir_path)
/     or
/ InportDXFfromDir(TEXT dir_path, INT srid, INT append, TEXT dims,
/                  TEXT mode, TEXT special_rings, TEXT table_prefix,
/                  TEXT layer_name)
/
/ returns:
/ 1 on success
/ or 0 on failure
/ NULL on invalid arguments
*/
    int ret;
    char *dir_path;
    int srid = -1;
    int append = 0;
    int special_rings = GAIA_DXF_RING_NONE;
    int mode = GAIA_DXF_IMPORT_BY_LAYER;
    int force_dims = GAIA_DXF_AUTO_2D_3D;
    char *prefix = NULL;
    char *layer_name = NULL;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    dir_path = (char *) sqlite3_value_text (argv[0]);
    if (argc > 7)
      {
	  const char *value;
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  srid = sqlite3_value_int (argv[1]);
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  append = sqlite3_value_int (argv[2]);
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[3]);
	  if (strcasecmp (value, "2D") == 0)
	      force_dims = GAIA_DXF_FORCE_2D;
	  else if (strcasecmp (value, "3D") == 0)
	      force_dims = GAIA_DXF_FORCE_3D;
	  else if (strcasecmp (value, "AUTO") == 0)
	      force_dims = GAIA_DXF_AUTO_2D_3D;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[4]);
	  if (strcasecmp (value, "MIXED") == 0)
	      mode = GAIA_DXF_IMPORT_MIXED;
	  else if (strcasecmp (value, "DISTINCT") == 0)
	      mode = GAIA_DXF_IMPORT_BY_LAYER;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  value = (const char *) sqlite3_value_text (argv[5]);
	  if (strcasecmp (value, "LINKED") == 0)
	      special_rings = GAIA_DXF_RING_LINKED;
	  else if (strcasecmp (value, "UNLINKED") == 0)
	      special_rings = GAIA_DXF_RING_UNLINKED;
	  else if (strcasecmp (value, "NONE") == 0)
	      special_rings = GAIA_DXF_RING_NONE;
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[6]) == SQLITE_TEXT)
	      prefix = (char *) sqlite3_value_text (argv[6]);
	  else if (sqlite3_value_type (argv[6]) != SQLITE_NULL)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[7]) == SQLITE_TEXT)
	      layer_name = (char *) sqlite3_value_text (argv[7]);
	  else if (sqlite3_value_type (argv[7]) != SQLITE_NULL)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

    ret =
	scan_dxf_dir (db_handle, cache, dir_path, srid, append, force_dims,
		      mode, special_rings, prefix, layer_name);
    sqlite3_result_int (context, ret);
}

#endif /* end GEOS conditional */

static void
fnct_ExportDXF (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ExportDXF(TEXT out_dir, TEXT filename, TEXT sql_query, TEXT layer_col_name,
/           TEXT geom_col_name, TEXT label_col_name, TEXT text_height_col_name,
/           TEXT text_rotation_col_name, BLOB geom_filter)
/     or
/ ExportDXF(TEXT out_dir, TEXT filename, TEXT sql_query, TEXT layer_col_name,
/           TEXT geom_col_name, TEXT label_col_name, TEXT text_height_col_name,
/           TEXT text_rotation_col_name, BLOB geom_filter, INT precision)
/
/ returns:
/ 1 on success
/ or 0 on failure
*/
    unsigned char *p_blob;
    int n_bytes;
    char *path;
    const char *dir_path = NULL;
    const char *filename = NULL;
    FILE *out = NULL;
    const char *sql_query = NULL;
    const char *layer_col_name = NULL;
    const char *geom_col_name = NULL;
    const char *label_col_name = NULL;
    const char *text_height_col_name = NULL;
    const char *text_rotation_col_name = NULL;
    gaiaGeomCollPtr geom = NULL;
    int precision = 3;
    int ret = 1;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	dir_path = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	filename = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	sql_query = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	layer_col_name = (const char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	geom_col_name = (const char *) sqlite3_value_text (argv[4]);
    if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
	label_col_name = (const char *) sqlite3_value_text (argv[5]);
    if (sqlite3_value_type (argv[6]) == SQLITE_TEXT)
	text_height_col_name = (const char *) sqlite3_value_text (argv[6]);
    if (sqlite3_value_type (argv[7]) == SQLITE_TEXT)
	text_rotation_col_name = (const char *) sqlite3_value_text (argv[7]);
    if (sqlite3_value_type (argv[8]) == SQLITE_BLOB)
      {
	  p_blob = (unsigned char *) sqlite3_value_blob (argv[8]);
	  n_bytes = sqlite3_value_bytes (argv[8]);
	  geom =
	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
					   gpkg_amphibious);
      }
    if (argc == 10)
      {
	  if (sqlite3_value_type (argv[9]) == SQLITE_INTEGER)
	      precision = sqlite3_value_int (argv[9]);
      }
    if (dir_path == NULL || filename == NULL || sql_query == NULL
	|| layer_col_name == NULL || geom_col_name == NULL)
      {
	  sqlite3_result_int (context, 0);
	  if (geom != NULL)
	      gaiaFreeGeomColl (geom);
	  return;
      }

    path = sqlite3_mprintf ("%s/%s.dxf", dir_path, filename);
    out = fopen (path, "wb");
    if (out == NULL)
      {
	  ret = 0;
	  spatialite_e ("ExportDXF error - unable to create \"%s\"\n", path);
      }
    else
      {
	  /* exporting the DXF */
	  gaiaDxfWriter dxf;
	  gaiaDxfWriterInit (&dxf, out, precision, GAIA_DXF_V12);
	  ret = gaiaExportDxf (&dxf, db_handle, sql_query, layer_col_name,
			       geom_col_name, label_col_name,
			       text_height_col_name, text_rotation_col_name,
			       geom);
	  if (ret > 0)
	      ret = 1;
	  fclose (out);
      }
    sqlite3_result_int (context, ret);
    if (geom != NULL)
	gaiaFreeGeomColl (geom);
    sqlite3_free (path);
}

static void
fnct_CheckDuplicateRows (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ CheckDuplicateRows(TEXT table)
/
/ returns:
/ the number of duplicate rows found
/ NULL on invalid arguments
*/
    char *table;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);

    check_duplicated_rows (db_handle, table, &rows);

    if (rows < 0)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_RemoveDuplicateRows (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RemoveDuplicateRows(TEXT table)
/ RemoveDuplicateRows(TEXT table, BOOL transaction)
/
/ returns:
/ the number of duplicate rows removed
/ NULL on invalid arguments
*/
    char *table;
    int transaction = 1;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  transaction = sqlite3_value_int (argv[1]);
      }

    remove_duplicated_rows_ex2 (db_handle, table, &rows, transaction);

    if (rows < 0)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_ElementaryGeometries (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ ElementaryGeometries(TEXT input_table, TEXT geo_column, TEXT out_table,
/                      TEXT out_pk, TEXT out_multi_id)
/ ElementaryGeometries(TEXT input_table, TEXT geo_column, TEXT out_table,
/                      TEXT out_pk, TEXT out_multi_id, BOOL transaction)
/ ElementaryGeometries(TEXT input_table, TEXT geo_column, TEXT out_table,
/                      TEXT out_pk, TEXT out_multi_id, BOOL transaction,
/                     ... text option1 ..., ... text option2 ..., text option10)
/
/ returns:
/ the number of inserted rows
/ NULL on invalid arguments
*/
    char *in_table;
    char *geo_column;
    char *out_table;
    char *out_pk;
    char *out_multi_id;
    const void *options = NULL;
    int rows;
    int transaction = 1;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    in_table = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geo_column = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    out_table = (char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    out_pk = (char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    out_multi_id = (char *) sqlite3_value_text (argv[4]);
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  transaction = sqlite3_value_int (argv[5]);
      }

/* additional options */
    if (argc > 6 && sqlite3_value_type (argv[6]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 7 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 7 && sqlite3_value_type (argv[7]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 8 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 8 && sqlite3_value_type (argv[8]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 9 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 9 && sqlite3_value_type (argv[9]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 10 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 10 && sqlite3_value_type (argv[10]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 11 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 11 && sqlite3_value_type (argv[11]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 12 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 12 && sqlite3_value_type (argv[12]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 13 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 13 && sqlite3_value_type (argv[13]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 14 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 14 && sqlite3_value_type (argv[14]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 15 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }
    if (argc > 15 && sqlite3_value_type (argv[15]) != SQLITE_TEXT)
      {
	  spatialite_e
	      ("ElementaryGeometries() error: argument 16 is not of the String or TEXT type\n");
	  sqlite3_result_null (context);
	  return;
      }

    options = gaiaElemGeomOptionsCreate ();
    if (options == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

/* additional options */
    if (argc > 6)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[6]));
    if (argc > 7)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[7]));
    if (argc > 8)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[8]));
    if (argc > 9)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[9]));
    if (argc > 10)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[10]));
    if (argc > 11)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[11]));
    if (argc > 12)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[12]));
    if (argc > 13)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[13]));
    if (argc > 14)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[14]));
    if (argc > 15)
	gaiaElemGeomOptionsAdd (options,
				(const char *) sqlite3_value_text (argv[15]));

    elementary_geometries_ex3 (db_handle, in_table, geo_column, out_table,
			       out_pk, out_multi_id, options, &rows,
			       transaction);

    gaiaElemGeomOptionsDestroy (options);
    if (rows <= 0)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_DropGeoTable (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ DropGeoTable(TEXT table)
/ DropGeoTable(TEXT table, BOOL transaction)
/ DropGeoTable(TEXT db_prefix, TEXT table)
/ DropGeoTable(TEXT db_prefix, TEXT table, BOOL transaction)
/
/ returns:
/ 1 on success, 0 on failure
/ NULL on invalid arguments
*/
    char *db_prefix = "main";
    char *table = NULL;
    int transaction = 1;
    int ret;
    int cnt;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  table = (char *) sqlite3_value_text (argv[0]);
      }
    else if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	      && sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		table = (char *) sqlite3_value_text (argv[0]);
		transaction = sqlite3_value_int (argv[1]);
	    }
	  else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
		   && sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	    {
		db_prefix = (char *) sqlite3_value_text (argv[0]);
		table = (char *) sqlite3_value_text (argv[1]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  transaction = sqlite3_value_int (argv[2]);
      }

    cnt = sqlite3_total_changes (db_handle);
    ret = gaiaDropTableEx3 (db_handle, db_prefix, table, transaction, NULL);
    if (ret)
      {
	  if (sqlite3_total_changes (db_handle) <= cnt)
	      ret = 0;
      }

    sqlite3_result_int (context, ret);
}

static void
fnct_sp_get_last_error (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_GetLastError()
/
/ return the most recent SQL Procedures error message (if any)
/ return NULL on any other case
*/
    const char *msg = NULL;
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (data != NULL)
	msg = gaia_sql_proc_get_last_error (data);
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_sp_set_logfile (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_SetLogfile(TEXT logfile_path)
/ SqlProc_SetLogfile(TEXT logfile_path, INT append)
/
/ returns:
/ 1 on success
/ raises an exception on invalid argument or unaccessible file
*/
    const char *filepath = NULL;
    int append = 0;
    char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	filepath = (const char *) sqlite3_value_text (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	;
    else
	goto invalid_filepath;
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	      append = sqlite3_value_int (argv[1]);
	  else
	      goto invalid_append;
      }
    if (gaia_sql_proc_logfile (cache, filepath, append))
	sqlite3_result_int (context, 1);
    else
	goto file_error;
    return;

  invalid_filepath:
    msg = "SqlProc exception - illegal File Path argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_append:
    msg = "SqlProc exception - illegal Append Mode argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  file_error:
    msg =
	sqlite3_mprintf
	("SqlProc exception - unable to open \"%s\" for writing.", filepath);
    sqlite3_result_error (context, msg, -1);
    sqlite3_free (msg);
    return;
}

static void
fnct_sp_get_logfile (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_GetLogfile(void)
/
/ returns:
/ the path of the currently set Logfile
/ NULL if no current Logfile is defined
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	sqlite3_result_null (context);
    else
      {
	  const char *path = cache->SqlProcLogfile;
	  if (path == NULL)
	      sqlite3_result_null (context);
	  else
	      sqlite3_result_text (context, path, strlen (path), SQLITE_STATIC);
      }
}

static void
fnct_sp_from_text (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_FromText(TEXT sql-script)
/ SqlProc_FromText(TEXT sql-script, TEXT charset)
/
/ returns:
/ a SQL Procedure BLOB object
/ raises an exception on invalid arguments
*/
    const char *sql;
    const char *charset = "UTF-8";
    unsigned char *blob = NULL;
    int blob_sz = 0;
    const char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_sql_body;
    sql = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	      goto invalid_charset;
	  charset = (const char *) sqlite3_value_text (argv[1]);
      }
    if (gaia_sql_proc_parse (cache, sql, charset, &blob, &blob_sz))
	sqlite3_result_blob (context, blob, blob_sz, free);
    else
      {
	  if (blob != NULL)
	      free (blob);
	  goto parse_error;
      }
    return;

  invalid_sql_body:
    msg = "SqlProc exception - illegal SQL Body argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_charset:
    msg = "SqlProc exception - illegal Charset Encodind argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  parse_error:
    msg = "SqlProc exception - invalid SQL Body.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_from_file (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_FromFile(TEXT filepath)
/ SqlProc_FromFile(TEXT filepath, TEXT charset)
/
/ returns:
/ a SQL Procedure BLOB object
/ raises an exception on invalid arguments
*/
    const char *filepath;
    const char *charset = "UTF-8";
    unsigned char *blob = NULL;
    int blob_sz = 0;
    const char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_filepath;
    filepath = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	      goto invalid_charset;
	  charset = (const char *) sqlite3_value_text (argv[1]);
      }
    if (gaia_sql_proc_import (cache, filepath, charset, &blob, &blob_sz))
	sqlite3_result_blob (context, blob, blob_sz, free);
    else
      {
	  if (blob != NULL)
	      free (blob);
	  goto read_error;
      }
    return;

  invalid_filepath:
    msg = "SqlProc exception - illegal File Path argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_charset:
    msg = "SqlProc exception - illegal Charset Encodind argument.";
    sqlite3_result_error (context, msg, -1);
    return;

  read_error:
    msg = "SqlProc exception - unable to parse the external File.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_is_valid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_IsValid(BLOB)
/
/ returns:
/ 1 if the argument really is a valid SQL Procedure Object
/ 0 if not
*/
    const unsigned char *blob;
    int blob_sz = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (gaia_sql_proc_is_valid (blob, blob_sz))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;
}

static void
fnct_sp_var_count (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_NumVariables(BLOB)
/
/ returns:
/ the total count of Variables from a SQL Procedure Object
/ raises an exception on invalid argument
*/
    const unsigned char *blob;
    int blob_sz = 0;
    int count;
    const char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    count = gaia_sql_proc_var_count (blob, blob_sz);
    sqlite3_result_int (context, count);
    return;

  invalid_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_variable (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_VariableN(BLOB, INTEGER index)
/
/ returns:
/ the Name of the Nth Variable from a SQL Procedure Object
/ raises an exception on invalid arguments
*/
    const unsigned char *blob;
    int blob_sz = 0;
    int count;
    int index;
    const char *varname;
    char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_blob_argument;
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	goto invalid_int_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    index = sqlite3_value_int (argv[1]);
    count = gaia_sql_proc_var_count (blob, blob_sz);
    if (index < 0 || index >= count)
	goto illegal_index;
    varname = gaia_sql_proc_variable (blob, blob_sz, index);
    if (varname == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, varname, strlen (varname), free);
    return;

  invalid_blob_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_int_argument:
    msg = "SqlProc exception - illegal Index arg [not an INTEGER].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;

  illegal_index:
    msg =
	sqlite3_mprintf
	("SqlProc exception - illegal Index (expected to be between 0 and %d).",
	 count - 1);
    sqlite3_result_error (context, msg, -1);
    sqlite3_free (msg);
    return;
}

static void
fnct_sp_all_variables (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_AllVariables(BLOB)
/
/ returns:
/ a space separated list of Variable Names from a SQL Procedure Object
/ raises an exception on invalid arguments
*/
    const unsigned char *blob;
    int blob_sz = 0;
    const char *varlist;
    const char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    varlist = gaia_sql_proc_all_variables (blob, blob_sz);
    if (varlist == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, varlist, strlen (varlist), sqlite3_free);
    return;

  invalid_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static int
is_decorated_variable_name (const char *name)
{
/* testing for an already decorated Variable Name */
    char mark_start = *name;
    char mark_stop = *(name + strlen (name) - 1);
    if (mark_start == mark_stop && (mark_start == '@' || mark_start == '$'))
	return 1;
    return 0;
}

static char *
do_encode_blob_value (const unsigned char *blob, int blob_sz)
{
/* encoding a BLOB value as an Hex String */
    char *value = sqlite3_malloc ((blob_sz * 2) + 4);
    char *p_out = value;
    int i;

    *p_out++ = 'x';
    *p_out++ = '\'';
    for (i = 0; i < blob_sz; i++)
      {
	  unsigned char byte = *(blob + i);
	  unsigned char hi = byte / 16;
	  unsigned char lo = byte - (hi * 16);
	  switch (hi)
	    {
	    case 0:
		*p_out++ = '0';
		break;
	    case 1:
		*p_out++ = '1';
		break;
	    case 2:
		*p_out++ = '2';
		break;
	    case 3:
		*p_out++ = '3';
		break;
	    case 4:
		*p_out++ = '4';
		break;
	    case 5:
		*p_out++ = '5';
		break;
	    case 6:
		*p_out++ = '6';
		break;
	    case 7:
		*p_out++ = '7';
		break;
	    case 8:
		*p_out++ = '8';
		break;
	    case 9:
		*p_out++ = '9';
		break;
	    case 10:
		*p_out++ = 'A';
		break;
	    case 11:
		*p_out++ = 'B';
		break;
	    case 12:
		*p_out++ = 'C';
		break;
	    case 13:
		*p_out++ = 'D';
		break;
	    case 14:
		*p_out++ = 'E';
		break;
	    case 15:
		*p_out++ = 'F';
		break;
	    };
	  switch (lo)
	    {
	    case 0:
		*p_out++ = '0';
		break;
	    case 1:
		*p_out++ = '1';
		break;
	    case 2:
		*p_out++ = '2';
		break;
	    case 3:
		*p_out++ = '3';
		break;
	    case 4:
		*p_out++ = '4';
		break;
	    case 5:
		*p_out++ = '5';
		break;
	    case 6:
		*p_out++ = '6';
		break;
	    case 7:
		*p_out++ = '7';
		break;
	    case 8:
		*p_out++ = '8';
		break;
	    case 9:
		*p_out++ = '9';
		break;
	    case 10:
		*p_out++ = 'A';
		break;
	    case 11:
		*p_out++ = 'B';
		break;
	    case 12:
		*p_out++ = 'C';
		break;
	    case 13:
		*p_out++ = 'D';
		break;
	    case 14:
		*p_out++ = 'E';
		break;
	    case 15:
		*p_out++ = 'F';
		break;
	    };
      }
    *p_out++ = '\'';
    *p_out = '\0';
    return value;
}

static void
fnct_sp_var_arg (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_VarValue(TEXT var-name, NULL)
/ SqlProc_VarValue(TEXT var-name, INT value)
/ SqlProc_VarValue(TEXT var-name, DOUBLE value)
/ SqlProc_VarValue(TEXT var-name, TEXT value)
/ SqlProc_VarValue(TEXT var-name, BLOB value)
/
/ returns:
/ an SQL Procedure Argument - Variable with Value
/ raises an exception on invalid arguments 
*/
    const char *var_name;
    const unsigned char *blob;
    int blob_sz;
    char *var_arg;
    const char *msg;
    char *value;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    var_name = (const char *) sqlite3_value_text (argv[0]);
    switch (sqlite3_value_type (argv[1]))
      {
      case SQLITE_NULL:
	  value = sqlite3_mprintf ("%s", "NULL");
	  break;
      case SQLITE_INTEGER:
#if defined(_WIN32) && !defined(__MINGW32__)
	  value = sqlite3_mprintf ("%I64d", sqlite3_value_int64 (argv[1]));
#else
	  value = sqlite3_mprintf ("%lld", sqlite3_value_int64 (argv[1]));
#endif
	  break;
      case SQLITE_FLOAT:
	  value = sqlite3_mprintf ("%1.10f", sqlite3_value_double (argv[1]));
	  break;
      case SQLITE_TEXT:
	  value = sqlite3_mprintf ("%s", sqlite3_value_text (argv[1]));
	  break;
      case SQLITE_BLOB:
      default:
	  blob = sqlite3_value_blob (argv[1]);
	  blob_sz = sqlite3_value_bytes (argv[1]);
	  value = do_encode_blob_value (blob, blob_sz);
	  break;
      };

    if (is_decorated_variable_name (var_name))
	var_arg = sqlite3_mprintf ("%s=%s", var_name, value);
    else
	var_arg = sqlite3_mprintf ("@%s@=%s", var_name, value);
    sqlite3_result_text (context, var_arg, strlen (var_arg), sqlite3_free);
    sqlite3_free (value);
    return;

  invalid_argument:
    msg = "SqlProc exception - illegal Name arg [not TEXT].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_is_valid_var (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_IsValidVarValue(TEXT)
/
/ returns:
/ 1 if the argument really is a valid SQL Variable with Value
/ 0 if not
*/
    const char *str;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    str = (const char *) sqlite3_value_text (argv[0]);
    if (gaia_sql_proc_is_valid_var_value (str))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;
}

static void
fnct_sp_raw_sql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_RawSQL(BLOB)
/
/ returns:
/ the raw SQL body from a SQL Procedure Object
/ raises an exception on invalid arguments 
*/
    const unsigned char *blob;
    int blob_sz = 0;
    const char *sql;
    const char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    sql = gaia_sql_proc_raw_sql (blob, blob_sz);
    if (sql == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, sql, strlen (sql), free);
    return;

  invalid_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static SqlProc_VarListPtr
get_sql_proc_variables (const void *cache, int argc, sqlite3_value ** argv)
{
/* common implementation: creating an SQL Procedure Variables List */
    int i;
    const char *text;
    SqlProc_VarListPtr list = gaia_sql_proc_create_variables ();
    for (i = 1; i < argc; i++)
      {
	  if (sqlite3_value_type (argv[i]) != SQLITE_TEXT)
	    {
		char *errmsg =
		    sqlite3_mprintf
		    ("Variable Argument #%d is not of the TEXT type.\n",
		     i - 1);
		gaia_sql_proc_set_error (cache, errmsg);
		sqlite3_free (errmsg);
		list->Error = 1;
		goto end;
	    }
	  text = (const char *) sqlite3_value_text (argv[i]);
	  if (!gaia_sql_proc_add_variable (list, text))
	    {
		char *errmsg =
		    sqlite3_mprintf ("Illegal Variable Argument #%d: %s\n",
				     i - 1, text);
		gaia_sql_proc_set_error (cache, errmsg);
		sqlite3_free (errmsg);
		list->Error = 1;
		goto end;
	    }
      }
  end:
    return list;
}

static void
fnct_sp_cooked_sql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_CookedSQL(BLOB [, arg1 TEXT, arg2 TEXT, ... argN TEXT] )
/
/ returns:
/ the cooked SQL body from a SQL Procedure Object
/ raises an exception on invalid arguments 
*/
    const unsigned char *blob;
    int blob_sz = 0;
    char *sql;
    const char *msg;
    SqlProc_VarListPtr variables = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_blob_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;

/* retrieving the List of Variables with Values */
    variables = get_sql_proc_variables (cache, argc, argv);
    if (variables == NULL)
	goto err_variables;
    if (variables->Error)
	goto illegal_variables;

/* replacing Variables */
    if (!gaia_sql_proc_cooked_sql
	(sqlite, cache, blob, blob_sz, variables, &sql))
	goto cooking_error;
    if (sql == NULL)
      {
	  sqlite3_result_null (context);
	  goto done;
      }
    sqlite3_result_text (context, sql, strlen (sql), free);

  done:
    gaia_sql_proc_destroy_variables (variables);
    return;

  invalid_blob_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;

  err_variables:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    msg = "SqlProc exception - unable to get a List of Variables with Values.";
    sqlite3_result_error (context, msg, -1);
    return;

  illegal_variables:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    msg =
	"SqlProc exception - the List of Variables with Values contains illegal items.";
    sqlite3_result_error (context, msg, -1);
    return;

  cooking_error:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    msg = "SqlProc exception - unable to create a Cooked SQL Body.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_execute (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_Execute(BLOB [, arg1 TEXT, arg2 TEXT, ... argN TEXT] )
/
/ returns:
/ 1 on succes
/ raises an exception on invalid arguments or errors
*/
    const unsigned char *blob;
    int blob_sz = 0;
    char *sql;
    const char *msg;
    SqlProc_VarListPtr variables = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
	goto invalid_blob_argument;
    blob = sqlite3_value_blob (argv[0]);
    blob_sz = sqlite3_value_bytes (argv[0]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;

/* retrieving the List of Variables with Values */
    variables = get_sql_proc_variables (cache, argc, argv);
    if (variables == NULL)
	goto err_variables;
    if (variables->Error)
	goto illegal_variables;

/* replacing Variables */
    if (!gaia_sql_proc_cooked_sql
	(sqlite, cache, blob, blob_sz, variables, &sql))
	goto cooking_error;

/* executing the SQL Procedure */
    if (!gaia_sql_proc_execute (sqlite, cache, sql))
	goto sql_error;
    sqlite3_result_int (context, 1);
    if (sql != NULL)
	free (sql);
    gaia_sql_proc_destroy_variables (variables);
    return;

  invalid_blob_argument:
    msg = "SqlProc exception - illegal SQL Procedure arg [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;

  err_variables:
    msg = "SqlProc exception - unable to get a List of Variables with Values.";
    sqlite3_result_error (context, msg, -1);
    return;

  illegal_variables:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    msg =
	"SqlProc exception - the List of Variables with Values contains illegal items.";
    sqlite3_result_error (context, msg, -1);
    return;

  cooking_error:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    msg = "SqlProc exception - unable to create a Cooked SQL Body.";
    sqlite3_result_error (context, msg, -1);
    return;

  sql_error:
    if (variables != NULL)
	gaia_sql_proc_destroy_variables (variables);
    if (sql != NULL)
	free (sql);
    msg = "SqlProc exception - a fatal SQL error was encountered.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_exit (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ SqlProc_Exit( void )
/ StoredProc_Exit( void )
/
/ returns:
/ 1 on succes
/ raises an exception on invalid arguments or errors
*/
    const char *msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	goto no_cache;

/* registrering an EXIT request */
    cache->SqlProcContinue = 0;
    sqlite3_result_int (context, 1);
    return;

  no_cache:
    msg = "SqlProc_Exit exception - unable to find a Connection Cache.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_create_tables (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_CreateTables()
/
/ will attempt to create the Stored Procedures tables if not
/ already existing
/
/ returns:
/ 1 on success, 0 on failure 
*/
    int ret;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    void *data = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    ret = gaia_stored_proc_create_tables (sqlite, data);
    sqlite3_result_int (context, ret);
}

static void
fnct_sp_register (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_Register(name TEXT, title TEXT, sql_body BLOB)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *title;
    const unsigned char *blob;
    int blob_sz = 0;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    if (sqlite3_value_type (argv[2]) != SQLITE_BLOB)
	goto invalid_argument_3;
    name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    blob = sqlite3_value_blob (argv[2]);
    blob_sz = sqlite3_value_bytes (argv[2]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    if (gaia_stored_proc_store (sqlite, cache, name, title, blob, blob_sz))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument_1:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"StoredProc exception - illegal Stored Procedure Title [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_3:
    msg = "StoredProc exception - illegal Stored Procedure Body [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "StoredProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_get (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_Get(name TEXT)
/
/ returns:
/ a SQL Procedure BLOB Object
/ raises an exception on invalid arguments 
*/
    const char *name;
    unsigned char *blob;
    int blob_sz = 0;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (gaia_stored_proc_fetch (sqlite, cache, name, &blob, &blob_sz))
	sqlite3_result_blob (context, blob, blob_sz, free);
    else
	sqlite3_result_null (context);
    return;

  invalid_argument:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_delete (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_Delete(name TEXT)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (gaia_stored_proc_delete (sqlite, cache, name))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_update_title (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_UpdateTitle(name TEXT, title TEXT)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *title;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    if (gaia_stored_proc_update_title (sqlite, cache, name, title))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument_1:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"StoredProc exception - illegal Stored Procedure Title [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_update_sql (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_UpdateSqlBody(name TEXT, sql BLOB)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const unsigned char *blob;
    int blob_sz = 0;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
	goto invalid_argument_2;
    name = (const char *) sqlite3_value_text (argv[0]);
    blob = sqlite3_value_blob (argv[1]);
    blob_sz = sqlite3_value_bytes (argv[1]);
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;
    if (gaia_stored_proc_update_sql (sqlite, cache, name, blob, blob_sz))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument_1:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg = "StoredProc exception - illegal Stored Procedure Body [not a BLOB].";
    sqlite3_result_error (context, msg, -1);
    return;

  not_an_sql_proc:
    msg = "StoredProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_stored_execute (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ StoredProc_Execute(TEXT [, arg1 TEXT, arg2 TEXT, ... argN TEXT] )
/
/ returns:
/ 1 on succes
/ raises an exception on invalid arguments or errors
*/
    const char *name;
    unsigned char *blob;
    int blob_sz = 0;
    char *sql;
    const char *msg;
    char *message;
    SqlProc_VarListPtr variables = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_name_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (!gaia_stored_proc_fetch (sqlite, cache, name, &blob, &blob_sz))
	goto invalid_stored_procedure;
    if (!gaia_sql_proc_is_valid (blob, blob_sz))
	goto not_an_sql_proc;

/* retrieving the List of Variables with Values */
    variables = get_sql_proc_variables (cache, argc, argv);
    if (variables == NULL)
	goto err_variables;
    if (variables->Error)
	goto illegal_variables;

/* replacing Variables */
    if (!gaia_sql_proc_cooked_sql
	(sqlite, cache, blob, blob_sz, variables, &sql))
	goto cooking_error;
    free (blob);

/* executing the SQL Procedure */
    if (!gaia_sql_proc_execute (sqlite, cache, sql))
	goto sql_error;
    sqlite3_result_int (context, 1);
    if (sql != NULL)
	free (sql);
    gaia_sql_proc_destroy_variables (variables);
    return;

  invalid_name_argument:
    msg =
	"StoredProc exception - illegal Stored Procedure Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_stored_procedure:
    message =
	sqlite3_mprintf
	("StoredProc exception - unable to retrive a Stored Procedure named \"%s\".",
	 name);
    sqlite3_result_error (context, message, -1);
    sqlite3_free (message);
    return;

  not_an_sql_proc:
    free (blob);
    msg = "SqlProc exception - invalid SQL Procedure BLOB.";
    sqlite3_result_error (context, msg, -1);
    return;

  err_variables:
    free (blob);
    msg = "SqlProc exception - unable to get a List of Variables with Values.";
    sqlite3_result_error (context, msg, -1);
    return;

  illegal_variables:
    free (blob);
    gaia_sql_proc_destroy_variables (variables);
    msg =
	"SqlProc exception - the List of Variables with Values contains illegal items.";
    sqlite3_result_error (context, msg, -1);
    return;

  cooking_error:
    gaia_sql_proc_destroy_variables (variables);
    free (blob);
    msg = "SqlProc exception - unable to create a Cooked SQL Body.";
    sqlite3_result_error (context, msg, -1);
    return;

  sql_error:
    if (sql != NULL)
	free (sql);
    gaia_sql_proc_destroy_variables (variables);
    msg = "SqlProc exception - a fatal SQL error was encountered.";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_register (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_Register(name TEXT, title TEXT, value NULL)
/ StoredVar_Register(name TEXT, title TEXT, value INT)
/ StoredVar_Register(name TEXT, title TEXT, value DOUBLE)
/ StoredVar_Register(name TEXT, title TEXT, value TEXT)
/ StoredVar_Register(name TEXT, title TEXT, value BLOB)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *title;
    const unsigned char *blob;
    int blob_sz;
    char *value = NULL;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    switch (sqlite3_value_type (argv[2]))
      {
      case SQLITE_NULL:
	  value = sqlite3_mprintf ("%s", "NULL");
	  break;
      case SQLITE_INTEGER:
	  value = sqlite3_mprintf ("%d", sqlite3_value_int (argv[2]));
	  break;
      case SQLITE_FLOAT:
	  value = sqlite3_mprintf ("%1.10f", sqlite3_value_double (argv[2]));
	  break;
      case SQLITE_TEXT:
	  value = sqlite3_mprintf ("%s", sqlite3_value_text (argv[2]));
	  break;
      case SQLITE_BLOB:
      default:
	  blob = sqlite3_value_blob (argv[2]);
	  blob_sz = sqlite3_value_bytes (argv[2]);
	  value = do_encode_blob_value (blob, blob_sz);
	  break;
      };
    if (gaia_stored_var_store (sqlite, cache, name, title, value))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    if (value != NULL)
	sqlite3_free (value);
    return;

  invalid_argument_1:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"StoredVar exception - illegal Stored Variable Title [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_get (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_Get(name TEXT)
/
/ returns:
/ a Variable with Value string
/ raises an exception on invalid arguments 
*/
    const char *name;
    char *value;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (!gaia_stored_var_fetch (sqlite, cache, name, 1, &value))
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, value, strlen (value), free);
    return;

  invalid_argument:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_get_value (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_GetValue(name TEXT)
/
/ returns:
/ a Variable with Value string
/ raises an exception on invalid arguments 
*/
    const char *name;
    char *value;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (!gaia_stored_var_fetch (sqlite, cache, name, 0, &value))
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, value, strlen (value), free);
    return;

  invalid_argument:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_delete (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_Delete(name TEXT)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument;
    name = (const char *) sqlite3_value_text (argv[0]);
    if (gaia_stored_var_delete (sqlite, cache, name))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_update_title (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_UpdateTitle(name TEXT, title TEXT)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments 
*/
    const char *name;
    const char *title;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    if (gaia_stored_var_update_title (sqlite, cache, name, title))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    return;

  invalid_argument_1:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"StoredVar exception - illegal Stored Variable Title [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_sp_var_update_value (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ StoredVar_UpdateValue(name TEXT, value NULL)
/ StoredVar_UpdateValue(name TEXT, value INT)
/ StoredVar_UpdateValue(name TEXT, value DOUBLE)
/ StoredVar_UpdateValue(name TEXT, value TEXT)
/ StoredVar_UpdateValue(name TEXT, value BLOB)
/
/ returns:
/ 1 on success, 0 on failure 
/ raises an exception on invalid arguments
*/
    const char *name;
    const unsigned char *blob;
    int blob_sz;
    char *value = NULL;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    name = (const char *) sqlite3_value_text (argv[0]);
    switch (sqlite3_value_type (argv[1]))
      {
      case SQLITE_NULL:
	  value = sqlite3_mprintf ("%s", "NULL");
	  break;
      case SQLITE_INTEGER:
#if defined(_WIN32) && !defined(__MINGW32__)
	  value = sqlite3_mprintf ("%I64d", sqlite3_value_int64 (argv[1]));
#else
	  value = sqlite3_mprintf ("%lld", sqlite3_value_int64 (argv[1]));
#endif
	  break;
      case SQLITE_FLOAT:
	  value = sqlite3_mprintf ("%1.10f", sqlite3_value_double (argv[1]));
	  break;
      case SQLITE_TEXT:
	  value = sqlite3_mprintf ("%s", sqlite3_value_text (argv[1]));
	  break;
      case SQLITE_BLOB:
      default:
	  blob = sqlite3_value_blob (argv[1]);
	  blob_sz = sqlite3_value_bytes (argv[1]);
	  value = do_encode_blob_value (blob, blob_sz);
	  break;
      };
    if (gaia_stored_var_update_value (sqlite, cache, name, value))
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
    if (value != NULL)
	sqlite3_free (value);
    return;

  invalid_argument_1:
    msg =
	"StoredVar exception - illegal Stored Variable Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_create_routing_nodes (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ CreateRoutingNodes(db-prefix TEXT, input-table TEXT , geom-column TEXT,
/                    node-from-column TEXT , node-to-column TEXT )
/
/ returns:
/ 1 on succes
/ raises an exception on invalid arguments or errors
*/
    const char *db_prefix;
    const char *input_table;
    const char *geom_column;
    const char *from_column;
    const char *to_column;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
	db_prefix = NULL;
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	db_prefix = (const char *) sqlite3_value_text (argv[0]);
    else
	goto invalid_argument_1;
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    else
	input_table = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
	geom_column = NULL;
    else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	geom_column = (const char *) sqlite3_value_text (argv[2]);
    else
	goto invalid_argument_3;
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	goto invalid_argument_4;
    from_column = (const char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	goto invalid_argument_5;
    to_column = (const char *) sqlite3_value_text (argv[4]);
    if (gaia_create_routing_nodes
	(sqlite, cache, db_prefix, input_table, geom_column, from_column,
	 to_column))
	sqlite3_result_int (context, 1);
    else
      {
	  /* there was an error, raising an Exception */
	  char *msg_err;
	  msg = gaia_create_routing_get_last_error (cache);
	  if (msg == NULL)
	      msg_err =
		  sqlite3_mprintf
		  ("CreateRoutingNodes exception - Unknown reason");
	  else
	      msg_err =
		  sqlite3_mprintf ("CreateRoutingNodes exception - %s", msg);
	  sqlite3_result_error (context, msg_err, -1);
	  sqlite3_free (msg_err);
      }
    return;

  invalid_argument_1:
    msg =
	"CreateRoutingNodes exception - illegal DB-prefix [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"CreateRoutingNodes exception - illegal Spatial-Table Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_3:
    msg =
	"CreateRoutingNodes exception - illegal Geometry Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_4:
    msg =
	"CreateRoutingNodes exception - illegal FromNode Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_5:
    msg =
	"CreateRoutingNodes exception - illegal ToNode Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_create_routing (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ CreateRouting(routing-data-table TEXT , virtual-routing-table TEXT , 
/               input-table TEXT , from-column TEXT , to-column TEXT , 
/               geom-column TEXT , cost-column TEXT )
/ CreateRouting(routing-data-table TEXT , virtual-routing-table TEXT , 
/               input-table TEXT , from-column TEXT , to-column TEXT , 
/               geom-column TEXT , cost-column TEXT , name-column TEXT ,
/               a-star-enabled BOOLEAN , bidirectional BOOLEAN )
/ CreateRouting(routing-data-table TEXT , virtual-routing-table TEXT , 
/               input-table TEXT , from-column TEXT , to-column TEXT , 
/               geom-column TEXT , cost-column TEXT , name-column TEXT ,
/               a-star-enabled BOOLEAN , bidirectional BOOLEAN ,
/               oneway-from TEXT , oneway-to TEXT )
/ CreateRouting(routing-data-table TEXT , virtual-routing-table TEXT , 
/               input-table TEXT , from-column TEXT , to-column TEXT , 
/               geom-column TEXT , cost-column TEXT , name-column TEXT ,
/               a-star-enabled BOOLEAN , bidirectional BOOLEAN ,
/               oneway-from TEXT , oneway-to TEXT , overwrite BOOLEAN )
/
/ returns:
/ 1 on succes
/ raises an exception on invalid arguments or errors
*/
    const char *routing_data_table;
    const char *virtual_routing_table;
    const char *input_table;
    const char *from_column;
    const char *to_column;
    const char *geom_column;
    const char *cost_column;
    const char *name_column;
    int a_star_enabled = 1;
    int bidirectional = 1;
    const char *oneway_from = NULL;
    const char *oneway_to = NULL;
    int overwrite = 0;
    const char *msg;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	goto invalid_argument_1;
    routing_data_table = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
	goto invalid_argument_2;
    virtual_routing_table = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
	goto invalid_argument_3;
    input_table = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	goto invalid_argument_4;
    from_column = (const char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	goto invalid_argument_5;
    to_column = (const char *) sqlite3_value_text (argv[4]);
    if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
	geom_column = NULL;
    else if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
	geom_column = (const char *) sqlite3_value_text (argv[5]);
    else
	goto invalid_argument_6;
    if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
	cost_column = NULL;
    else if (sqlite3_value_type (argv[6]) == SQLITE_TEXT)
	cost_column = (const char *) sqlite3_value_text (argv[6]);
    else
	goto invalid_argument_7;
    if (argc >= 10)
      {
	  if (sqlite3_value_type (argv[7]) == SQLITE_NULL)
	      name_column = NULL;
	  else if (sqlite3_value_type (argv[7]) == SQLITE_TEXT)
	      name_column = (const char *) sqlite3_value_text (argv[7]);
	  else
	      goto invalid_argument_8;
	  if (sqlite3_value_type (argv[8]) != SQLITE_INTEGER)
	      goto invalid_argument_9;
	  a_star_enabled = sqlite3_value_int (argv[8]);
	  if (sqlite3_value_type (argv[9]) != SQLITE_INTEGER)
	      goto invalid_argument_10;
	  bidirectional = sqlite3_value_int (argv[9]);
      }
    if (argc >= 12)
      {
	  if (sqlite3_value_type (argv[10]) == SQLITE_NULL)
	      oneway_from = NULL;
	  else if (sqlite3_value_type (argv[10]) == SQLITE_TEXT)
	      oneway_from = (const char *) sqlite3_value_text (argv[10]);
	  else
	      goto invalid_argument_11;
	  if (sqlite3_value_type (argv[11]) == SQLITE_NULL)
	      oneway_to = NULL;
	  else if (sqlite3_value_type (argv[11]) == SQLITE_TEXT)
	      oneway_to = (const char *) sqlite3_value_text (argv[11]);
	  else
	      goto invalid_argument_12;
      }
    if (argc >= 13)
      {
	  if (sqlite3_value_type (argv[12]) != SQLITE_INTEGER)
	      goto invalid_argument_13;
	  overwrite = sqlite3_value_int (argv[12]);
      }
    if (gaia_create_routing
	(sqlite, cache, routing_data_table, virtual_routing_table,
	 input_table, from_column, to_column, geom_column, cost_column,
	 name_column, a_star_enabled, bidirectional, oneway_from, oneway_to,
	 overwrite))
	sqlite3_result_int (context, 1);
    else
      {
	  /* there was an error, raising an Exception */
	  char *msg_err;
	  msg = gaia_create_routing_get_last_error (cache);
	  if (msg == NULL)
	      msg_err =
		  sqlite3_mprintf ("CreateRouting exception - Unknown reason");
	  else
	      msg_err = sqlite3_mprintf ("CreateRouting exception - %s", msg);
	  sqlite3_result_error (context, msg_err, -1);
	  sqlite3_free (msg_err);
      }
    return;

  invalid_argument_1:
    msg =
	"CreateRouting exception - illegal Routing-Data Table Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_2:
    msg =
	"CreateRouting exception - illegal VirtualRouting-Table Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_3:
    msg =
	"CreateRouting exception - illegal Input-Table Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_4:
    msg =
	"CreateRouting exception - illegal FromNode Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_5:
    msg =
	"CreateRouting exception - illegal ToNode Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_6:
    msg =
	"CreateRouting exception - illegal Geometry Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_7:
    msg =
	"CreateRouting exception - illegal Cost Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_8:
    msg =
	"CreateRouting exception - illegal RoadName Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_9:
    msg =
	"CreateRouting exception - illegal A* Enabled option [not an INTEGER].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_10:
    msg =
	"CreateRouting exception - illegal Bidirectional option [not an INTEGER].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_11:
    msg =
	"CreateRouting exception - illegal OnewayFromTo Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_12:
    msg =
	"CreateRouting exception - illegal OnewayToFrom Column Name [not a TEXT string].";
    sqlite3_result_error (context, msg, -1);
    return;

  invalid_argument_13:
    msg =
	"CreateRouting exception - illegal OverWrite option [not an INTEGER].";
    sqlite3_result_error (context, msg, -1);
    return;
}

static void
fnct_create_routing_get_last_error (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
/* SQL function:
/ CreateRouting_GetLastError()
/
/ returns:
/ the most recent error message raised by CreateRouting
/ or NULL if no such message is available
*/
    const char *err_msg;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

    err_msg = gaia_create_routing_get_last_error (cache);
    if (err_msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, err_msg, strlen (err_msg), SQLITE_STATIC);
}

#ifndef OMIT_FREEXL		/* FREEXL is enabled */
static void
fnct_ImportXLS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ImportXLS(TEXT filename, TEXT table)
/ ImportXLS(TEXT filename, TEXT table, INT worksheet_index)
/ ImportXLS(TEXT filename, TEXT table, INT worksheet_index,
/          INT first_line_titles)
/
/ returns:
/ the number of inserted rows
/ NULL on invalid arguments
*/
    const char *filename;
    const char *table;
    int widx;
    unsigned int worksheet_index = 0;
    int first_line_titles = 0;
    int ret;
    unsigned int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    filename = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (const char *) sqlite3_value_text (argv[1]);
    if (argc > 2)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  widx = sqlite3_value_int (argv[2]);
	  if (widx < 0)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  worksheet_index = widx;
      }
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  first_line_titles = sqlite3_value_int (argv[3]);
      }

    ret =
	load_XL (db_handle, filename, table, worksheet_index,
		 first_line_titles, &rows, NULL);

    if (!ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}
#endif /* end FREEXL support */

#ifndef OMIT_ICONV		/* ICONV is supported */
static void
fnct_ImportDBF (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ImportDBF(TEXT filename, TEXT table, TEXT charset)
/ ImportDBF(TEXT filename, TEXT table, TEXT charset, TEXT pk_column)
/ ImportDBF(TEXT filename, TEXT table, TEXT charset, TEXT pk_column,
/           INTEGER text_dates)
/ ImportDBF(TEXT filename, TEXT table, TEXT charset, TEXT pk_column,
/           INTEGER text_dates, INTEGER colname_case)
/
/ returns:
/ the number of inserted rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *path;
    char *charset;
    char *pk_column = NULL;
    int text_dates = 0;
    int colname_case = GAIA_DBF_COLNAME_LOWERCASE;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    charset = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      pk_column = (char *) sqlite3_value_text (argv[3]);
      }
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      text_dates = sqlite3_value_int (argv[4]);
      }
    if (argc > 5)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	    {
		const char *val = (char *) sqlite3_value_text (argv[5]);
		if (strcasecmp (val, "UPPER") == 0
		    || strcasecmp (val, "UPPERCASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_UPPERCASE;
		else if (strcasecmp (val, "SAME") == 0
			 || strcasecmp (val, "SAMECASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_CASE_IGNORE;
		else
		    colname_case = GAIA_DBF_COLNAME_LOWERCASE;
	    }
      }

    ret =
	load_dbf_ex3 (db_handle, path, table, pk_column, charset, 1,
		      text_dates, &rows, colname_case, NULL);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_ExportDBF (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ExportDBF(TEXT table, TEXT filename, TEXT charset)
/ ExportDBF(TEXT table, TEXT filename, TEXT charset, TEXT colname_case)
/
/ returns:
/ the number of exported rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *path;
    char *charset;
    int rows;
    int colname_case = GAIA_DBF_COLNAME_LOWERCASE;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    charset = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	    {
		const char *val = (char *) sqlite3_value_text (argv[3]);
		if (strcasecmp (val, "UPPER") == 0
		    || strcasecmp (val, "UPPERCASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_UPPERCASE;
		else if (strcasecmp (val, "SAME") == 0
			 || strcasecmp (val, "SAMECASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_CASE_IGNORE;
		else
		    colname_case = GAIA_DBF_COLNAME_LOWERCASE;
	    }
      }

    ret =
	dump_dbf_ex2 (db_handle, table, path, charset, &rows, colname_case,
		      NULL);

    if (rows <= 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_ImportSHP (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ImportSHP(TEXT filename, TEXT table, TEXT charset)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d) 
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed) 
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed, INT spatial_index) 
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed, INT spatial_index,
/           INT text_dates)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed, INT spatial_index,
/           INT text_dates, TEXT colname_case)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed, INT spatial_index,
/           INT text_dates, TEXT colname_case, INT update_statistics)
/ ImportSHP(TEXT filename, TEXT table, TEXT charset, INT srid, 
/           TEXT geom_column, TEXT pk_column, TEXT geom_type,
/           INT coerce2d, INT compressed, INT spatial_index,
/           INT text_dates, TEXT colname_case, INT update_statistics,
/           INT verbose)
/
/ returns:
/ the number of imported rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *path;
    char *charset;
    int srid = -1;
    int coerce2d = 0;
    int compressed = 0;
    int spatial_index = 0;
    int text_dates = 0;
    int update_statistics = 1;
    int verbose = 1;
    char *pk_column = NULL;
    char *geo_column = NULL;
    char *geom_type = NULL;
    int colname_case = GAIA_DBF_COLNAME_LOWERCASE;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    charset = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      srid = sqlite3_value_int (argv[3]);
      }
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      geo_column = (char *) sqlite3_value_text (argv[4]);
      }
    if (argc > 5)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      pk_column = (char *) sqlite3_value_text (argv[5]);
      }
    if (argc > 6)
      {
	  if (sqlite3_value_type (argv[6]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      geom_type = (char *) sqlite3_value_text (argv[6]);
      }
    if (argc > 7)
      {
	  if (sqlite3_value_type (argv[7]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      coerce2d = sqlite3_value_int (argv[7]);
      }
    if (argc > 8)
      {
	  if (sqlite3_value_type (argv[8]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      compressed = sqlite3_value_int (argv[8]);
      }
    if (argc > 9)
      {
	  if (sqlite3_value_type (argv[9]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      spatial_index = sqlite3_value_int (argv[9]);
      }
    if (argc > 10)
      {
	  if (sqlite3_value_type (argv[10]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      text_dates = sqlite3_value_int (argv[10]);
      }
    if (argc > 11)
      {
	  if (sqlite3_value_type (argv[11]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	    {
		const char *val = (char *) sqlite3_value_text (argv[11]);
		if (strcasecmp (val, "UPPER") == 0
		    || strcasecmp (val, "UPPERCASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_UPPERCASE;
		else if (strcasecmp (val, "SAME") == 0
			 || strcasecmp (val, "SAMECASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_CASE_IGNORE;
		else
		    colname_case = GAIA_DBF_COLNAME_LOWERCASE;
	    }
      }
    if (argc > 12)
      {
	  if (sqlite3_value_type (argv[12]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      update_statistics = sqlite3_value_int (argv[12]);
      }
    if (argc > 13)
      {
	  if (sqlite3_value_type (argv[13]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      verbose = sqlite3_value_int (argv[13]);
      }

    ret =
	load_shapefile_ex3 (db_handle, path, table, charset, srid, geo_column,
			    geom_type, pk_column, coerce2d, compressed,
			    verbose, spatial_index, text_dates, &rows,
			    colname_case, NULL);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
      {
	  if (update_statistics)
	      update_layer_statistics (db_handle, table, NULL);
	  sqlite3_result_int (context, rows);
      }
}

static void
fnct_ExportSHP (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ExportSHP(TEXT table, TEXT geom_column, TEXT filename, TEXT charset)
/ ExportSHP(TEXT table, TEXT geom_column, TEXT filename, TEXT charset,
/           TEXT geom_type)
/ ExportSHP(TEXT table, TEXT geom_column, TEXT filename, TEXT charset,
/           TEXT geom_type, TEXT colname_case)
/
/ returns:
/ the number of exported rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *column;
    char *path;
    char *charset;
    char *geom_type = NULL;
    int colname_case = GAIA_DBF_COLNAME_CASE_IGNORE;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    column = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    charset = (char *) sqlite3_value_text (argv[3]);
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      geom_type = (char *) sqlite3_value_text (argv[4]);
      }
    if (argc > 5)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	    {
		const char *val = (char *) sqlite3_value_text (argv[5]);
		if (strcasecmp (val, "UPPER") == 0
		    || strcasecmp (val, "UPPERCASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_UPPERCASE;
		else if (strcasecmp (val, "SAME") == 0
			 || strcasecmp (val, "SAMECASE") == 0)
		    colname_case = GAIA_DBF_COLNAME_CASE_IGNORE;
		else
		    colname_case = GAIA_DBF_COLNAME_LOWERCASE;
	    }
      }

    ret =
	dump_shapefile_ex (db_handle, table, column, path, charset, geom_type,
			   1, &rows, colname_case, NULL);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

#endif /* end ICONV supported */

static void
fnct_ExportKML (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ExportKML(TEXT table, TEXT geom_column, TEXT filename)
/ ExportKML(TEXT table, TEXT geom_column, TEXT filename, INT precision)
/ ExportKML(TEXT table, TEXT geom_column, TEXT filename, INT precision,
/           TEXT name_column)
/ ExportKML(TEXT table, TEXT geom_column, TEXT filename, INT precision,
/           TEXT name_column, TEXT description_column)
/
/ returns:
/ the number of exported rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *geom_col;
    char *path;
    int precision = 8;
    char *name_col = NULL;
    char *descr_col = NULL;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geom_col = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      precision = sqlite3_value_int (argv[3]);
      }
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      name_col = (char *) sqlite3_value_text (argv[4]);
      }
    if (argc > 5)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      descr_col = (char *) sqlite3_value_text (argv[5]);
      }

    ret =
	dump_kml_ex (db_handle, table, geom_col, path, name_col, descr_col,
		     precision, &rows);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

static void
fnct_ExportGeoJSON (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ExportGeoJSON(TEXT table, TEXT geom_column, TEXT filename)
/ ExportGeoJSON(TEXT table, TEXT geom_column, TEXT filename, 
/               TEXT format)
/ ExportGeoJSON(TEXT table, TEXT geom_column, TEXT filename, 
/               TEXT format, INT precision)
/
/ returns:
/ the number of exported rows
/ NULL on invalid arguments
*/
    int ret;
    char *table;
    char *geom_col;
    char *path;
    int format = 0;
    int precision = 8;
    char *fmt = NULL;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    geom_col = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	    {
		fmt = (char *) sqlite3_value_text (argv[3]);
		if (strcasecmp (fmt, "none") == 0)
		    format = 0;
		else if (strcasecmp (fmt, "MBR") == 0)
		    format = 1;
		else if (strcasecmp (fmt, "withShortCRS") == 0)
		    format = 2;
		else if (strcasecmp (fmt, "MBRwithShortCRS") == 0)
		    format = 3;
		else if (strcasecmp (fmt, "withLongCRS") == 0)
		    format = 4;
		else if (strcasecmp (fmt, "MBRwithLongCRS") == 0)
		    format = 5;
		else
		  {
		      sqlite3_result_null (context);
		      return;
		  }
	    }
      }
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      precision = sqlite3_value_int (argv[4]);
      }

    ret =
	dump_geojson_ex (db_handle, table, geom_col, path, precision, format,
			 &rows);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}

#ifdef ENABLE_LIBXML2		/* including LIBXML2 */
static void
wfs_page_done (int features, void *ptr)
{
/* WFS progress handler callback */
    if (ptr == NULL)
	ptr = NULL;		/* silencing stupid compiler warnings */
    if (isatty (1))
	spatialite_e ("WFS Features loaded since now: %d\r", features);
}

static void
fnct_ImportWFS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ImportWFS(TEXT filename_or_url, TEXT layer_name, TEXT table)
/ ImportWFS(TEXT filename_or_url, TEXT layer_name, TEXT table,
/           TEXT pk_column)
/ ImportWFS(TEXT filename_or_url, TEXT layer_name, TEXT table,
/           TEXT pk_column, INT swap_axes)
/ ImportWFS(TEXT filename_or_url, TEXT layer_name, TEXT table,
/           TEXT pk_column, INT swap_axes, INT page_size)
/ ImportWFS(TEXT filename_or_url, TEXT layer_name, TEXT table,
/           TEXT pk_column, INT swap_axes, INT page_size,
/           INT spatial_index)
/
/ returns:
/ the number of imported rows
/ NULL on invalid arguments
*/
    int ret;
    char *path_or_url;
    char *layer_name;
    char *table;
    int swap_axes = 0;
    int spatial_index = 0;
    int page_size = -1;
    char *pk_column = NULL;
    int rows;
    sqlite3 *db_handle = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path_or_url = (char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    layer_name = (char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    table = (char *) sqlite3_value_text (argv[2]);
    if (argc > 3)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      pk_column = (char *) sqlite3_value_text (argv[3]);
      }
    if (argc > 4)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      swap_axes = sqlite3_value_int (argv[4]);
      }
    if (argc > 5)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      page_size = sqlite3_value_int (argv[5]);
      }
    if (argc > 6)
      {
	  if (sqlite3_value_type (argv[6]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  else
	      spatial_index = sqlite3_value_int (argv[6]);
      }

    ret =
	load_from_wfs_paged (db_handle, path_or_url, NULL, layer_name,
			     swap_axes, table, pk_column, spatial_index,
			     page_size, &rows, NULL, wfs_page_done, NULL);

    if (rows < 0 || !ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, rows);
}
#endif /* end including LIBXML2 */

static int
is_word_delimiter (const char x, int post)
{
/* testing for a SQL word delimiter */
    if (x == ' ' || x == '\t' || x == '\n' || x == '\r' || x == '(')
	return 1;
    if (post && x == '(')
	return 1;
    if (!post && x == ',')
	return 1;
    return 0;
}

static int
do_check_eval (const char *str)
{
/* testing if a Trigger attempts calling the dangerous Eval() function */

    int contains_eval = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "eval");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 4);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_eval = 1;
	  start = ptr + 4;
      }
    return contains_eval;
}

static int
do_check_blob_from_file (const char *str)
{
/* testing if a Trigger attempts calling the dangerous BlobFromFile() function */

    int contains_blob_from_file = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "blobfromfile");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 12);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_blob_from_file = 1;
	  start = ptr + 12;
      }
    return contains_blob_from_file;
}

static int
do_check_blob_to_file (const char *str)
{
/* testing if a Trigger attempts calling the dangerous BlobToFile() function */

    int contains_blob_to_file = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "blobtofile");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 10);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_blob_to_file = 1;
	  start = ptr + 10;
      }
    return contains_blob_to_file;
}

static int
do_check_load_xml (const char *str)
{
/* testing if a Trigger attempts calling the dangerous XB_LoadXml() function */

    int contains_load_xml = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "xb_loadxml");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 10);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_load_xml = 1;
	  start = ptr + 10;
      }
    return contains_load_xml;
}

static int
do_check_store_xml (const char *str)
{
/* testing if a Trigger attempts calling the dangerous XB_StoreXml() function */

    int contains_store_xml = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "xb_storexml");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 11);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_store_xml = 1;
	  start = ptr + 11;
      }
    return contains_store_xml;
}

static int
do_check_export_geo_json (const char *str)
{
/* testing if a Trigger attempts calling the dangerous ExportGeoJSON() function */

    int contains_export_geo_json = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, "exportgeojson");
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 13);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_export_geo_json = 1;
	  start = ptr + 13;
      }
    return contains_export_geo_json;
}

static int
do_check_impexp (const char *str, const char *ref)
{
/* 
/ testing if a Trigger attempts calling one of the dangerous ExportDXF(), 
/ ImportDXF(), ExportDBF(), ImportDBF(),  ExportSHP(), ExportSHP(),
/ ExportKML(), ImportWFS() or ImportXLS() functions */

    int contains_impexp = 0;
    const char *start = str;
    const char *ptr;
    while (1)
      {
	  char pre;
	  char post;
	  ptr = strstr (start, ref);
	  if (ptr == NULL)
	      break;
	  if (ptr > str)
	      pre = *(ptr - 1);
	  else
	      pre = ' ';
	  post = *(ptr + 9);
	  if (is_word_delimiter (pre, 0) && is_word_delimiter (post, 1))
	      contains_impexp = 1;
	  start = ptr + 9;
      }
    return contains_impexp;
}

static void
fnct_CountUnsafeTriggers (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ CountUnsafeTriggers()
/
/ returns:
/ the total count of *unsafe* triggers found
/ 0 if no dubious trigger has been identified
*/
    int ret;
    int i;
    char **results;
    int rows;
    int columns;
    const char *sql;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int count = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* checking all Triggers */
    sql = "SELECT Lower(sql) FROM sqlite_master WHERE "
	"type IN ('trigger', 'view') AND (sql LIKE '%BlobFromFile%' "
	"OR sql LIKE '%BlobToFile%' OR sql LIKE '%XB_LoadXML%' "
	"OR sql LIKE '%XB_StoreXML%' OR sql LIKE '%ImportDXF%' "
	"OR sql LIKE '%ExportDXF%' OR sql LIKE '%ImportDBF%' "
	"OR sql LIKE '%ExportDBF%' OR sql LIKE '%ImportSHP%' "
	"OR sql LIKE '%ExportSHP%' OR sql LIKE '%ExportKML%' "
	"OR sql LIKE '%ExportGeoJSON%' OR (sql LIKE '%eval%' AND sql LIKE '%(%') "
	"OR sql LIKE '%ImportWFS%' OR sql LIKE '%ImportXLS%')";
    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
    if (ret != SQLITE_OK)
	goto unknown;
    if (rows < 1)
	;
    else
      {
	  for (i = 1; i <= rows; i++)
	    {
		int dangerous = 0;
		if (do_check_blob_from_file (results[(i * columns) + 0]))
		    dangerous = 1;
		if (do_check_blob_to_file (results[(i * columns) + 0]))
		    dangerous = 1;
		if (do_check_load_xml (results[(i * columns) + 0]))
		    dangerous = 1;
		if (do_check_store_xml (results[(i * columns) + 0]))
		    dangerous = 1;
		if (do_check_export_geo_json (results[(i * columns) + 0]))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "importdxf"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "exportdxf"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "importdbf"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "exportdbf"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "importshp"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "exportshp"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "exportkml"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "importwfs"))
		    dangerous = 1;
		if (do_check_impexp (results[(i * columns) + 0], "importxls"))
		    dangerous = 1;
		if (do_check_eval (results[(i * columns) + 0]))
		    dangerous = 1;
		if (dangerous)
		    count++;
	    }
      }
    sqlite3_free_table (results);
  unknown:
    sqlite3_result_int (context, count);
}

static void
fnct_GeodesicLength (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicLength(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns  the total Geodesic length for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double l;
    double length = 0.0;
    double a;
    double b;
    double rf;
    gaiaGeomCollPtr geo = NULL;
    gaiaLinestringPtr line;
    gaiaPolygonPtr polyg;
    gaiaRingPtr ring;
    int ib;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (getEllipsoidParams (sqlite, geo->Srid, &a, &b, &rf))
	    {
		line = geo->FirstLinestring;
		while (line)
		  {
		      /* Linestrings */
		      l = gaiaGeodesicTotalLength (a, b, rf,
						   line->DimensionModel,
						   line->Coords, line->Points);
		      if (l < 0.0)
			{
			    length = -1.0;
			    break;
			}
		      length += l;
		      line = line->Next;
		  }
		if (length >= 0)
		  {
		      /* Polygons */
		      polyg = geo->FirstPolygon;
		      while (polyg)
			{
			    /* exterior Ring */
			    ring = polyg->Exterior;
			    l = gaiaGeodesicTotalLength (a, b, rf,
							 ring->DimensionModel,
							 ring->Coords,
							 ring->Points);
			    if (l < 0.0)
			      {
				  length = -1.0;
				  break;
			      }
			    length += l;
			    for (ib = 0; ib < polyg->NumInteriors; ib++)
			      {
				  /* interior Rings */
				  ring = polyg->Interiors + ib;
				  l = gaiaGeodesicTotalLength (a, b, rf,
							       ring->
							       DimensionModel,
							       ring->Coords,
							       ring->Points);
				  if (l < 0.0)
				    {
					length = -1.0;
					break;
				    }
				  length += l;
			      }
			    if (length < 0.0)
				break;
			    polyg = polyg->Next;
			}
		  }
		if (length < 0.0)
		    sqlite3_result_null (context);
		else
		    sqlite3_result_double (context, length);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_GreatCircleLength (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ GreatCircleLength(BLOB encoded GEOMETRYCOLLECTION)
/
/ returns  the total Great Circle length for current geometry 
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    double length = 0.0;
    double a;
    double b;
    double rf;
    gaiaGeomCollPtr geo = NULL;
    gaiaLinestringPtr line;
    gaiaPolygonPtr polyg;
    gaiaRingPtr ring;
    int ib;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geo =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    if (!geo)
	sqlite3_result_null (context);
    else
      {
	  if (getEllipsoidParams (sqlite, geo->Srid, &a, &b, &rf))
	    {
		line = geo->FirstLinestring;
		while (line)
		  {
		      /* Linestrings */
		      length +=
			  gaiaGreatCircleTotalLength (a, b,
						      line->DimensionModel,
						      line->Coords,
						      line->Points);
		      line = line->Next;
		  }
		if (length >= 0)
		  {
		      /* Polygons */
		      polyg = geo->FirstPolygon;
		      while (polyg)
			{
			    /* exterior Ring */
			    ring = polyg->Exterior;
			    length +=
				gaiaGreatCircleTotalLength (a, b,
							    ring->
							    DimensionModel,
							    ring->Coords,
							    ring->Points);
			    for (ib = 0; ib < polyg->NumInteriors; ib++)
			      {
				  /* interior Rings */
				  ring = polyg->Interiors + ib;
				  length +=
				      gaiaGreatCircleTotalLength (a, b,
								  ring->
								  DimensionModel,
								  ring->Coords,
								  ring->Points);
			      }
			    polyg = polyg->Next;
			}
		  }
		sqlite3_result_double (context, length);
	    }
	  else
	      sqlite3_result_null (context);
	  gaiaFreeGeomColl (geo);
      }
}

static void
fnct_GeodesicArcLength (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicArcLength(geom1 Geometry, geom2 Geometry)
/ GeodesicArcLength(geom1 Geometry, geom2 Geometry, meters Integer)
/
/ returns the Arc Length between points 1 and 2
/ or NULL if any error is encountered
/
/ *******************************************************************
/ this SQL function was kindly contributed by Mark Johnson 
/ <mj10777@googlemail.com>
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int return_type = GAIA_GEODESIC_ARC_LENGTH_METERS;
    double retval;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_int (argv[2]) == 0)
	      return_type = GAIA_GEODESIC_ARC_LENGTH_DEGREES;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);

    if (geom1 == NULL || geom2 == NULL)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaGeodesicArcLength
	      (sqlite, cache, geom1, geom2, return_type, &retval))
	      sqlite3_result_double (context, retval);
	  else
	      sqlite3_result_null (context);
      }
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
}

static void
fnct_GeodesicChordLength (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicChordLength(geom1 Geometry, geom2 Geometry)
/ GeodesicChordLength(geom1 Geometry, geom2 Geometry, meters Integer)
/
/ returns the Chord Length between points 1 and 2
/ or NULL if any error is encountered
/
/ *******************************************************************
/ this SQL function was kindly contributed by Mark Johnson 
/ <mj10777@googlemail.com>
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int return_type = GAIA_GEODESIC_CHORD_LENGTH_METERS;
    double retval;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_int (argv[2]) == 0)
	      return_type = GAIA_GEODESIC_CHORD_LENGTH_DEGREES;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);

    if (!geom1 || !geom2)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaGeodesicArcLength
	      (sqlite, cache, geom1, geom2, return_type, &retval))
	      sqlite3_result_double (context, retval);
	  else
	      sqlite3_result_null (context);
      }
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
}

static void
fnct_GeodesicCentralAngle (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicCentralAngle(geom1 Geometry, geom2 Geometry)
/ GeodesicCentralAngle(geom1 Geometry, geom2 Geometry, radians Integer)
/
/ returns the Central Angle for the Arc between points 1 and 2
/ or NULL if any error is encountered
/
/ *******************************************************************
/ this SQL function was kindly contributed by Mark Johnson 
/ <mj10777@googlemail.com>
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    int return_type = GAIA_GEODESIC_CENTRAL_ANGLE_RADIANS;
    double retval;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_int (argv[2]) == 0)
	      return_type = GAIA_GEODESIC_CENTRAL_ANGLE_DEGREES;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);

    if (!geom1 || !geom2)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaGeodesicArcLength
	      (sqlite, cache, geom1, geom2, return_type, &retval))
	      sqlite3_result_double (context, retval);
	  else
	      sqlite3_result_null (context);
      }
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
}

static void
fnct_GeodesicArcArea (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicArcArea(geom1 Geometry, geom2 Geometry)
/
/ returns the Area (in sq. meters) for the Arc between points 1 and 2
/ or NULL if any error is encountered
/
/ *******************************************************************
/ this SQL function was kindly contributed by Mark Johnson 
/ <mj10777@googlemail.com>
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    double retval;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);

    if (!geom1 || !geom2)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaGeodesicArcLength
	      (sqlite, cache, geom1, geom2, GAIA_GEODESIC_ARC_AREA_METERS,
	       &retval))
	      sqlite3_result_double (context, retval);
	  else
	      sqlite3_result_null (context);
      }
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
}

static void
fnct_GeodesicArcHeight (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ GeodesicArcHeight(geom1 Geometry, geom2 Geometry)
/
/ returns the Height (in meters) for the Arc between points 1 and 2
/ or NULL if any error is encountered
/
/ *******************************************************************
/ this SQL function was kindly contributed by Mark Johnson 
/ <mj10777@googlemail.com>
*/
    unsigned char *p_blob;
    int n_bytes;
    gaiaGeomCollPtr geom1 = NULL;
    gaiaGeomCollPtr geom2 = NULL;
    double retval;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);
    p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    geom2 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
				     gpkg_amphibious);

    if (!geom1 || !geom2)
	sqlite3_result_null (context);
    else
      {
	  if (gaiaGeodesicArcLength
	      (sqlite, cache, geom1, geom2, GAIA_GEODESIC_ARC_HEIGHT_METERS,
	       &retval))
	      sqlite3_result_double (context, retval);
	  else
	      sqlite3_result_null (context);
      }
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    if (geom2 != NULL)
	gaiaFreeGeomColl (geom2);
}

static void
convertUnit (sqlite3_context * context, int argc, sqlite3_value ** argv,
	     int unit_from, int unit_to)
{
/* SQL functions:
/ CvtToKm(), CvtToDm(), CvtToCm(), CvtToMm(), CvtToKmi(), CvtToIn(), CvtToFt(),
/ CvtToYd(), CvtToMi(), CvtToFath(), CvtToCh(), CvtToLink(), CvtToUsIn(), 
/ CvtToUsFt(), CvtToUsYd(), CvtToUsCh(), CvtToUsMi(), CvtToIndFt(), 
/ CvtToIndYd(), CvtToIndCh(), 
/ CvtFromKm(), CvtFromDm(), CvtFromCm(), CvtFromMm(), CvtFromKmi(), 
/ CvtFromIn(), CvtFromFt(), CvtFromYd(), CvtFromMi(), CvtFromFath(), 
/ CvtFromCh(), CvtFromLink(), CvtFromUsIn(), CvtFromUsFt(), CvtFromUsYd(), 
/ CvtFromUsCh(), CvtFromUsMi(), CvtFromIndFt(), CvtFromIndYd(), 
/ CvtFromIndCh()
/
/ converts a Length from one unit to a different one
/ or NULL if any error is encountered
*/
    double cvt;
    double value;
    int int_value;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	value = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  value = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!gaiaConvertLength (value, unit_from, unit_to, &cvt))
	sqlite3_result_null (context);
    else
	sqlite3_result_double (context, cvt);
}

static void
fnct_cvtToKm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_KM);
}

static void
fnct_cvtToDm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_DM);
}

static void
fnct_cvtToCm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_CM);
}

static void
fnct_cvtToMm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_MM);
}

static void
fnct_cvtToKmi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_KMI);
}

static void
fnct_cvtToIn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_IN);
}

static void
fnct_cvtToFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_FT);
}

static void
fnct_cvtToYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_YD);
}

static void
fnct_cvtToMi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_MI);
}

static void
fnct_cvtToFath (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_FATH);
}

static void
fnct_cvtToCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_CH);
}

static void
fnct_cvtToLink (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_LINK);
}

static void
fnct_cvtToUsIn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_US_IN);
}

static void
fnct_cvtToUsFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_US_FT);
}

static void
fnct_cvtToUsYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_US_YD);
}

static void
fnct_cvtToUsCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_US_CH);
}

static void
fnct_cvtToUsMi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_US_MI);
}

static void
fnct_cvtToIndFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_IND_FT);
}

static void
fnct_cvtToIndYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_IND_YD);
}

static void
fnct_cvtToIndCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_M, GAIA_IND_CH);
}

static void
fnct_cvtFromKm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_KM, GAIA_M);
}

static void
fnct_cvtFromDm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_DM, GAIA_M);
}

static void
fnct_cvtFromCm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_CM, GAIA_M);
}

static void
fnct_cvtFromMm (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_MM, GAIA_M);
}

static void
fnct_cvtFromKmi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_KMI, GAIA_M);
}

static void
fnct_cvtFromIn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_IN, GAIA_M);
}

static void
fnct_cvtFromFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_FT, GAIA_M);
}

static void
fnct_cvtFromYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_YD, GAIA_M);
}

static void
fnct_cvtFromMi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_MI, GAIA_M);
}

static void
fnct_cvtFromFath (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_FATH, GAIA_M);
}

static void
fnct_cvtFromCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_CH, GAIA_M);
}

static void
fnct_cvtFromLink (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_LINK, GAIA_M);
}

static void
fnct_cvtFromUsIn (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_US_IN, GAIA_M);
}

static void
fnct_cvtFromUsFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_US_FT, GAIA_M);
}

static void
fnct_cvtFromUsYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_US_YD, GAIA_M);
}

static void
fnct_cvtFromUsCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_US_CH, GAIA_M);
}

static void
fnct_cvtFromUsMi (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_US_MI, GAIA_M);
}

static void
fnct_cvtFromIndFt (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_IND_FT, GAIA_M);
}

static void
fnct_cvtFromIndYd (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_IND_YD, GAIA_M);
}

static void
fnct_cvtFromIndCh (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    convertUnit (context, argc, argv, GAIA_IND_CH, GAIA_M);
}

static void
fnct_longFromDMS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LongitudeFromDMS ( dms_string )
/
/ return the Longitude (in Decimal Degrees) from a DMS text expression
/ or NULL if any error is encountered
*/
    const char *dms;
    double longitude;
    double latitude;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	dms = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!gaiaParseDMS (dms, &longitude, &latitude))
	sqlite3_result_null (context);
    else
	sqlite3_result_double (context, longitude);
}

static void
fnct_latFromDMS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LatitudeFromDMS ( dms_string )
/
/ return the Latitude (in Decimal Degrees) from a DMS text expression
/ or NULL if any error is encountered
*/
    const char *dms;
    double longitude;
    double latitude;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	dms = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (!gaiaParseDMS (dms, &longitude, &latitude))
	sqlite3_result_null (context);
    else
	sqlite3_result_double (context, latitude);
}

static void
fnct_toDMS (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ LongLatToDMS ( longitude, latitude )
/
/ return a DMS text expression
/ or NULL if any error is encountered
*/
    double longitude;
    double latitude;
    char *dms;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	longitude = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int value = sqlite3_value_int (argv[0]);
	  longitude = value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	latitude = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int value = sqlite3_value_int (argv[1]);
	  latitude = value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    dms = gaiaConvertToDMS (longitude, latitude);
    if (dms == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, dms, strlen (dms), free);
}

static void
fnct_sequence_currval (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ sequence_currval ( seq_name TEXT )
/
/ return the current value from some Sequence
/ or NULL if any error is encountered
*/
    const char *seq_name = NULL;
    gaiaSequencePtr seq;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	seq_name = (const char *) sqlite3_value_text (argv[0]);
    seq = gaiaFindSequence (cache, seq_name);
    if (seq == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, seq->value);
}

static void
fnct_sequence_lastval (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ sequence_lastval ()
/
/ return the last value returned by sequence_nextval()
/ or NULL if any error is encountered
*/
    int value;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (gaiaLastUsedSequence (cache, &value) == 0)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, value);
}

static void
fnct_sequence_nextval (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ sequence_nextval ( seq_name TEXT )
/
/ return the next value from some Sequence
/ or NULL if any error is encountered
*/
    const char *seq_name = NULL;
    gaiaSequencePtr seq;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	seq_name = (const char *) sqlite3_value_text (argv[0]);
    seq = gaiaFindSequence (cache, seq_name);
    if (seq == NULL)
      {
	  seq = gaiaCreateSequence (cache, seq_name);
	  if (seq == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		gaiaSequenceNext (cache, seq);
		sqlite3_result_int (context, seq->value);
	    }
      }
    else
      {
	  gaiaSequenceNext (cache, seq);
	  sqlite3_result_int (context, seq->value);
      }
}

static void
fnct_sequence_setval (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ sequence_setval ( seq_name TEXT , value INT )
/
/ resets the next value for some Sequence and return the value itself
/ or NULL if any error is encountered
*/
    const char *seq_name = NULL;
    int value;
    gaiaSequencePtr seq;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	seq_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	value = sqlite3_value_int (argv[1]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    seq = gaiaFindSequence (cache, seq_name);
    if (seq == NULL)
      {
	  seq = gaiaCreateSequence (cache, seq_name);
	  if (seq == NULL)
	      sqlite3_result_null (context);
	  else
	    {
		gaiaResetSequence (seq, value);
		sqlite3_result_int (context, seq->value);
	    }
      }
    else
      {
	  gaiaResetSequence (seq, value);
	  sqlite3_result_int (context, seq->value);
      }
}

#ifdef ENABLE_LIBXML2		/* including LIBXML2 */

static void
fnct_CreateStylingTables (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ CreateStylingTables()
/  or
/ CreateStylingTables(bool relaxed)
/  or
/ CreateStylingTables(bool relaxed, bool transaction)
/
/ creates any SLD/SE related table 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int relaxed = 0;
    int transaction = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  relaxed = sqlite3_value_int (argv[0]);
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  transaction = sqlite3_value_int (argv[1]);
      }

    if (!createStylingTables_ex (sqlite, relaxed, transaction))
	goto error;
    updateSpatiaLiteHistory (sqlite, "*** SE Styling ***", NULL,
			     "Styling tables successfully created");
    sqlite3_result_int (context, 1);
    return;

  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_RegisterExternalGraphic (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ RegisterExternalGraphic(String xlink_href, BLOB resource)
/  or
/ RegisterExternalGraphic(String xlink_href, BLOB resource, string title,
/						  String abstract, String file_name)
/
/ insert or updates an External Graphic 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *xlink_href;
    const char *title = NULL;
    const char *abstract = NULL;
    const char *file_name = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc == 5)
      {
	  /* optional extra args */
	  if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    xlink_href = (const char *) sqlite3_value_text (argv[0]);
    p_blob = sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    if (argc == 5)
      {
	  title = (const char *) sqlite3_value_text (argv[2]);
	  abstract = (const char *) sqlite3_value_text (argv[3]);
	  file_name = (const char *) sqlite3_value_text (argv[4]);
      }
    ret = register_external_graphic (sqlite, xlink_href, p_blob, n_bytes,
				     title, abstract, file_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterExternalGraphic (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ UnregisterExternalGraphic(String xlink_href)
/
/ removes an External Graphic 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *xlink_href;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    xlink_href = (const char *) sqlite3_value_text (argv[0]);
    ret = unregister_external_graphic (sqlite, xlink_href);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVectorCoverage (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVectorCoverage(Text coverage_name, Text f_table_name,
/                        Text f_geometry_column)
/   or
/ RegisterVectorCoverage(Text coverage_name, Text f_table_name,
/                        Text f_geometry_column, Text title,
/                        Text abstract)
/   or
/ RegisterVectorCoverage(Text coverage_name, Text f_table_name,
/                        Text f_geometry_column, Text title,
/                        Text abstract, Bool is_queryable,
/                        Bool is_editable)
/
/ inserts a Vector Coverage
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *f_table_name;
    const char *f_geometry_column;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = 0;
    int is_editable = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    f_table_name = (const char *) sqlite3_value_text (argv[1]);
    f_geometry_column = (const char *) sqlite3_value_text (argv[2]);
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[3]);
	  abstract = (const char *) sqlite3_value_text (argv[4]);
      }
    if (argc >= 7)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER
	      || sqlite3_value_type (argv[6]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[5]);
	  is_editable = sqlite3_value_int (argv[6]);
      }
    ret =
	register_vector_coverage (sqlite, coverage_name, f_table_name,
				  f_geometry_column, title, abstract,
				  is_queryable, is_editable);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterSpatialViewCoverage (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ RegisterSpatialViewCoverage(Text coverage_name, Text view_name,
/                             Text view_geometry)
/   or
/ RegisterSpatialViewCoverage(Text coverage_name, Text view_name,
/                             Text view_geometry, Text title,
/                             Text abstract)
/   or
/ RegisterSpatialViewCoverage(Text coverage_name, Text view_name,
/                             Text view_geometry, Text title,
/                             Text abstract, Bool is_queryable,
/                             Bool is_editable)
/
/ inserts a Vector Coverage based upon a Spatial View
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *view_name;
    const char *view_geometry;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = 0;
    int is_editable = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    view_name = (const char *) sqlite3_value_text (argv[1]);
    view_geometry = (const char *) sqlite3_value_text (argv[2]);
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[3]);
	  abstract = (const char *) sqlite3_value_text (argv[4]);
      }
    if (argc >= 7)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER
	      || sqlite3_value_type (argv[6]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[5]);
	  is_editable = sqlite3_value_int (argv[6]);
      }
    ret =
	register_spatial_view_coverage (sqlite, coverage_name, view_name,
					view_geometry, title, abstract,
					is_queryable, is_editable);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVirtualShapeCoverage (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVirtualShapeCoverage(Text coverage_name, Text virt_name,
/                              Text virt_geometry)
/   or
/ RegisterVirtualShapeCoverage(Text coverage_name, Text virt_name,
/                              Text virt_geometry, Text title,
/                              Text abstract)
/   or
/ RegisterVirtualShapeCoverage(Text coverage_name, Text virt_name,
/                              Text virt_geometry, Text title,
/                              Text abstract, Bool is_queryable)
/
/ inserts a Vector Coverage based upon a VirtualShapefile
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *virt_name;
    const char *virt_geometry;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    virt_name = (const char *) sqlite3_value_text (argv[1]);
    virt_geometry = (const char *) sqlite3_value_text (argv[2]);
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[4]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[3]);
	  abstract = (const char *) sqlite3_value_text (argv[4]);
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[5]);
      }
    ret =
	register_virtual_shp_coverage (sqlite, coverage_name, virt_name,
				       virt_geometry, title, abstract,
				       is_queryable);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterTopoGeoCoverage (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ RegisterTopoGeoCoverage(Text coverage_name, Text topogeo_name)
/   or
/ RegisterTopoGeoCoverage(Text coverage_name, Text topogeo_name,
/                         Text title, Text abstract)
/   or
/ RegisterTopoGeoCoverage(Text coverage_name, Text topogeo_name,
/                         Text title, Text abstract, Bool is_queryable,
/                         Bool is_editable)
/
/ inserts a Vector Coverage based on some Topology-Geometry
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *topogeo_name;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = 0;
    int is_editable = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    topogeo_name = (const char *) sqlite3_value_text (argv[1]);
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[2]);
	  abstract = (const char *) sqlite3_value_text (argv[3]);
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER
	      || sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[4]);
	  is_editable = sqlite3_value_int (argv[5]);
      }
    ret =
	register_topogeo_coverage (sqlite, coverage_name, topogeo_name,
				   title, abstract, is_queryable, is_editable);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterTopoNetCoverage (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ RegisterTopoNetCoverage(Text coverage_name, Text toponet_name)
/   or
/ RegisterTopoNetCoverage(Text coverage_name, Text toponet_name,
/                         Text title, Text abstract)
/   or
/ RegisterTopoNetCoverage(Text coverage_name, Text toponet_name,
/                         Text title, Text abstract, Bool is_queryable,
/                         Bool is_editable)
/
/ inserts a Vector Coverage based on some Topology-Network
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *toponet_name;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = 0;
    int is_editable = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    toponet_name = (const char *) sqlite3_value_text (argv[1]);
    if (argc >= 4)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_TEXT
	      || sqlite3_value_type (argv[3]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  title = (const char *) sqlite3_value_text (argv[2]);
	  abstract = (const char *) sqlite3_value_text (argv[3]);
      }
    if (argc >= 6)
      {
	  if (sqlite3_value_type (argv[4]) != SQLITE_INTEGER
	      || sqlite3_value_type (argv[5]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[4]);
	  is_editable = sqlite3_value_int (argv[5]);
      }
    ret =
	register_toponet_coverage (sqlite, coverage_name, toponet_name,
				   title, abstract, is_queryable, is_editable);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterVectorCoverage (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterVectorCoverage(Text coverage_name)
/
/ deletes a Vector Coverage
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    ret = unregister_vector_coverage (sqlite, coverage_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetVectorCoverageInfos (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
/* SQL function:
/ SetVectorCoverageInfos(Text coverage_name, Text title,
/                        Text abstract)
/    or
/ SetVectorCoverageInfos(Text coverage_name, Text title,
/                        Text abstract, Bool is_queryable,
/                        Bool is_editable)
/
/ updates the descriptive infos supporting a Vector Coverage
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *title = NULL;
    const char *abstract = NULL;
    int is_queryable = -1;
    int is_editable = -1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    abstract = (const char *) sqlite3_value_text (argv[2]);
    if (argc >= 5)
      {
	  if (sqlite3_value_type (argv[3]) != SQLITE_INTEGER
	      || sqlite3_value_type (argv[4]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  is_queryable = sqlite3_value_int (argv[3]);
	  is_editable = sqlite3_value_int (argv[4]);
      }
    ret =
	set_vector_coverage_infos (sqlite, coverage_name, title, abstract,
				   is_queryable, is_editable);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetVectorCoverageCopyright (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ SetVectorCoverageCopyright(Text coverage_name, Text copyright)
/    or
/ SetVectorCoverageCopyright(Text coverage_name, Text copyright,
/                            Text license)
/
/ updates copyright infos supporting a Vector Coverage
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *copyright = NULL;
    const char *license = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
	;
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	copyright = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    if (argc >= 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      license = (const char *) sqlite3_value_text (argv[2]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    ret =
	set_vector_coverage_copyright (sqlite, coverage_name, copyright,
				       license);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVectorCoverageSrid (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVectorCoverageSrid(Text coverage_name, Integer srid)
/
/ inserts a Vector Coverage alternative SRID
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    srid = sqlite3_value_int (argv[1]);
    ret = register_vector_coverage_srid (sqlite, coverage_name, srid);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterVectorCoverageSrid (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterVectorCoverageSrid(Text coverage_name, Integer srid)
/
/ deletes a Vector Coverage alternative SRID
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    srid = sqlite3_value_int (argv[1]);
    ret = unregister_vector_coverage_srid (sqlite, coverage_name, srid);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVectorCoverageKeyword (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVectorCoverageKeyword(Text coverage_name, Text keyword)
/
/ inserts a Vector Coverage Keyword
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *keyword;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    keyword = (const char *) sqlite3_value_text (argv[1]);
    ret = register_vector_coverage_keyword (sqlite, coverage_name, keyword);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterVectorCoverageKeyword (sqlite3_context * context, int argc,
				      sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterVectorCoverageSrid(Text coverage_name, Text keyword)
/
/ deletes a Vector Coverage Keyword
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *keyword;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    keyword = (const char *) sqlite3_value_text (argv[1]);
    ret = unregister_vector_coverage_keyword (sqlite, coverage_name, keyword);
    sqlite3_result_int (context, ret);
}

static void
fnct_UpdateVectorCoverageExtent (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ UpdateVectorCoverageExtent()
/   or
/ UpdateVectorCoverageExtent(Integer transaction)
/   or
/ UpdateVectorCoverageExtent(Text coverage_name)
/   or
/ UpdateVectorCoverageExtent(Text coverage_name, int transaction)
/
/ updates Vector Coverage Extents
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name = NULL;
    int transaction = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	      coverage_name = (const char *) sqlite3_value_text (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	      transaction = sqlite3_value_int (argv[0]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  coverage_name = (const char *) sqlite3_value_text (argv[0]);
	  transaction = sqlite3_value_int (argv[1]);
      }
    ret =
	update_vector_coverage_extent (sqlite, cache, coverage_name,
				       transaction);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVectorStyle (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVectorStyle(BLOB style)
/
/ inserts a Vector Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = register_vector_style (sqlite, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterVectorStyle (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterVectorStyle(Integer style_id [ , Integer removeAll] )
/  or
/ UnRegisterVectorStyle(Text style_name [ , Integer removeAll] )
/
/ removes a Vector Style definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    int remove_all = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  remove_all = sqlite3_value_int (argv[1]);
      }
    ret = unregister_vector_style (sqlite, style_id, style_name, remove_all);
    sqlite3_result_int (context, ret);
}

static void
fnct_ReloadVectorStyle (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ReloadVectorStyle(Integer style_id, BLOB style)
/    or
/ ReloadVectorStyle(Text style_name, BLOB style)
/
/ updates a Vector Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    ret = reload_vector_style (sqlite, style_id, style_name, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterVectorStyledLayer (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ RegisterVectorStyledLayer(String coverage_name, Integer style_id)
/  or
/ RegisterVectorStyledLayer(String coverage_name, Text style_name)
/
/ inserts a Vector Styled Layer 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret = register_vector_styled_layer_ex (sqlite, coverage_name, style_id,
					   style_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterVectorStyledLayer (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterVectorStyledLayer(String coverage_name,	Integer style_id)
/  or
/ UnRegisterVectorStyledLayer(String coverage_name,	Text style_name)
/
/ removes a Vector Styled Layer definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret = unregister_vector_styled_layer (sqlite, coverage_name, style_id,
					  style_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterRasterStyle (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RegisterRasterStyle(BLOB style)
/
/ inserts a Raster Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = register_raster_style (sqlite, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterRasterStyle (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterRasterStyle(Integer style_id [ , Integer removeAll] )
/   or
/ UnRegisterRasterStyledLayer(Text style_name [ , Integer removeAll] )
/
/ removes a Raster Style definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    int remove_all = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  remove_all = sqlite3_value_int (argv[1]);
      }
    ret = unregister_raster_style (sqlite, style_id, style_name, remove_all);
    sqlite3_result_int (context, ret);
}

static void
fnct_ReloadRasterStyle (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ ReloadRasterStyle(Integer style_id, BLOB style)
/    or
/ ReloadRasterStyle(Text style_name, BLOB style)
/
/ updates a Raster Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    ret = reload_raster_style (sqlite, style_id, style_name, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterRasterStyledLayer (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ RegisterRasterStyledLayer(String coverage_name, Integer style_id)
/   or
/ RegisterRasterStyledLayer(String coverage_name, Text style_name)
/
/ inserts a Raster Styled Layer 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	register_raster_styled_layer_ex (sqlite, coverage_name, style_id,
					 style_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterRasterStyledLayer (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterRasterStyledLayer(String coverage_name, Integer style_id)
/   or
/ UnRegisterRasterStyledLayer(String coverage_name, Text style_name)
/
/ removes a Raster Styled Layer definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	unregister_raster_styled_layer (sqlite, coverage_name, style_id,
					style_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterRasterCoverageSrid (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ RegisterRasterCoverageSrid(Text coverage_name, Integer srid)
/
/ inserts a Raster Coverage alternative SRID
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    srid = sqlite3_value_int (argv[1]);
    ret = register_raster_coverage_srid (sqlite, coverage_name, srid);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterRasterCoverageSrid (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterRasterCoverageSrid(Text coverage_name, Integer srid)
/
/ deletes a Raster Coverage alternative SRID
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    int srid;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    srid = sqlite3_value_int (argv[1]);
    ret = unregister_raster_coverage_srid (sqlite, coverage_name, srid);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterRasterCoverageKeyword (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
/* SQL function:
/ RegisterRasterCoverageKeyword(Text coverage_name, Text keyword)
/
/ inserts a Raster Coverage Keyword
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *keyword;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    keyword = (const char *) sqlite3_value_text (argv[1]);
    ret = register_raster_coverage_keyword (sqlite, coverage_name, keyword);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnregisterRasterCoverageKeyword (sqlite3_context * context, int argc,
				      sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterRasterCoverageKeyword(Text coverage_name, Text keyword)
/
/ deletes a Raster Coverage Keyword
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name;
    const char *keyword;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT
	|| sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    coverage_name = (const char *) sqlite3_value_text (argv[0]);
    keyword = (const char *) sqlite3_value_text (argv[1]);
    ret = unregister_raster_coverage_keyword (sqlite, coverage_name, keyword);
    sqlite3_result_int (context, ret);
}

static void
fnct_UpdateRasterCoverageExtent (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ UpdateRasterCoverageExtent()
/   or
/ UpdateRasterCoverageExtent(Integer transaction)
/   or
/ UpdateRasterCoverageExtent(Text coverage_name)
/   or
/ UpdateRasterCoverageExtent(Text coverage_name, int transaction)
/
/ updates Raster Coverage Extents
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *coverage_name = NULL;
    int transaction = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc >= 1)
      {
	  if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	      coverage_name = (const char *) sqlite3_value_text (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	      transaction = sqlite3_value_int (argv[0]);
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  coverage_name = (const char *) sqlite3_value_text (argv[0]);
	  transaction = sqlite3_value_int (argv[1]);
      }
    ret =
	update_raster_coverage_extent (sqlite, cache, coverage_name,
				       transaction);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterStyledGroupRaster (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ RegisterStyledGroupRaster(String group_name, String coverage_name)
/
/ inserts a Styled Group Raster item 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret = register_styled_group_ex (sqlite, group_name, NULL, coverage_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterStyledGroupVector (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ RegisterStyledGroupVector(String group_name, String coverage_name)
/
/ inserts a Styled Group Vector item 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret = register_styled_group_ex (sqlite, group_name, coverage_name, NULL);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetStyledGroupLayerPaintOrder (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
/* SQL function:
/ SetStyledGroupLayerPaintOrder(Integer item_id, Integer paint_order)
/
/ sets the paint order for a Styled Layer within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int item_id = -1;
    int paint_order = -1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    item_id = sqlite3_value_int (argv[0]);
    paint_order = sqlite3_value_int (argv[1]);
    ret =
	set_styled_group_layer_paint_order (sqlite, item_id, NULL, NULL, NULL,
					    paint_order);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetStyledGroupVectorPaintOrder (sqlite3_context * context, int argc,
				     sqlite3_value ** argv)
{
/* SQL function:
/ SetStyledGroupVectorPaintOrder(String group_name, String coverage_name, 
/                               Integer paint_order))
/
/ sets the paint order for a Vector Styled Layer within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    int paint_order = -1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
	  paint_order = sqlite3_value_int (argv[2]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	set_styled_group_layer_paint_order (sqlite, -1, group_name,
					    coverage_name, NULL, paint_order);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetStyledGroupRasterPaintOrder (sqlite3_context * context, int argc,
				     sqlite3_value ** argv)
{
/* SQL function:
/ SetStyledGroupRasterPaintOrder(String group_name, String coverage_name, 
/                               Integer paint_order))
/
/ sets the paint order for a Raster Styled Layer within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    int paint_order = -1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
	  paint_order = sqlite3_value_int (argv[2]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	set_styled_group_layer_paint_order (sqlite, -1, group_name,
					    NULL, coverage_name, paint_order);
    sqlite3_result_int (context, ret);
}

static void
fnct_SetStyledGroupInfos (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ SetStyledGroupInfos(String group_name, String title, 
/		      String abstract)
/
/ inserts or updates the descriptive infos supporting a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name;
    const char *title = NULL;
    const char *abstract = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    group_name = (const char *) sqlite3_value_text (argv[0]);
    title = (const char *) sqlite3_value_text (argv[1]);
    abstract = (const char *) sqlite3_value_text (argv[2]);
    ret = styled_group_set_infos (sqlite, group_name, title, abstract);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterStyledGroup (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterStyledGroup(String group_name)
/
/ removes a Styled Group and any related item 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    group_name = (const char *) sqlite3_value_text (argv[0]);
    ret = unregister_styled_group (sqlite, group_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterStyledGroupLayer (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterStyledGroupLayer(Integer item_id)
/
/ removes a Styled Layer from within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int item_id = -1;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    item_id = sqlite3_value_int (argv[0]);
    ret = unregister_styled_group_layer (sqlite, item_id, NULL, NULL, NULL);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterStyledGroupVector (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterStyledGroupVector(String group_name, String coverage_name)
/
/ removes a Vector Styled Layer from within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	unregister_styled_group_layer (sqlite, -1, group_name, coverage_name,
				       NULL);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterStyledGroupRaster (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterStyledGroupRaster(String group_name, String coverage_name)
/
/ removes a Raster Styled Layer from within a Styled Group 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name = NULL;
    const char *coverage_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT
	&& sqlite3_value_type (argv[1]) == SQLITE_TEXT)
      {
	  group_name = (const char *) sqlite3_value_text (argv[0]);
	  coverage_name = (const char *) sqlite3_value_text (argv[1]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	unregister_styled_group_layer (sqlite, -1, group_name, NULL,
				       coverage_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterGroupStyle (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ RegisterGroupStyle(BLOB style)
/
/ inserts a Group Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = register_group_style_ex (sqlite, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterGroupStyle (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterGroupStyle(Integer style_id [ , Integer removeAll] )
/   or
/ UnRegisterGroupStyledLayer(Text style_name [ , Integer removeAll] )
/
/ removes a Group Style definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    int remove_all = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  remove_all = sqlite3_value_int (argv[1]);
      }
    ret = unregister_group_style (sqlite, style_id, style_name, remove_all);
    sqlite3_result_int (context, ret);
}

static void
fnct_ReloadGroupStyle (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ ReloadGroupStyle(Integer style_id, BLOB style)
/    or
/ ReloadGroupStyle(Text style_name, BLOB style)
/
/ updates a Group Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    int style_id = -1;
    const char *style_name = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    ret = reload_group_style (sqlite, style_id, style_name, p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_RegisterStyledGroupStyle (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ RegisterStyleGroupStyle(String group_name, Integer style_id)
/   or
/ RegisterStyledGroupStyle(String group_name, Text style_name)
/
/ inserts a Styled Group Style 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    group_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	register_styled_group_style (sqlite, group_name, style_id, style_name);
    sqlite3_result_int (context, ret);
}

static void
fnct_UnRegisterStyledGroupStyle (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ UnRegisterStyledGroupStyle(String group_name, Integer style_id)
/   or
/ UnRegisterStyledGroupStyle(String group_name, Text style_name)
/
/ removes a Styled Group Style definition
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *group_name;
    int style_id = -1;
    const char *style_name = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    group_name = (const char *) sqlite3_value_text (argv[0]);
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	style_id = sqlite3_value_int (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
	style_name = (const char *) sqlite3_value_text (argv[1]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    ret =
	unregister_styled_group_style (sqlite, group_name, style_id,
				       style_name);
    sqlite3_result_int (context, ret);
}


static void
fnct_CreateIsoMetadataTables (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ CreateIsoMetadataTables()
/  or
/ CreateIsoMetadataTables(bool relaxed)
/
/ creates any ISO Metadata related table 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int relaxed = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (argc == 1)
      {
	  if (sqlite3_value_type (argv[0]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
	  relaxed = sqlite3_value_int (argv[0]);
      }

    if (!createIsoMetadataTables (sqlite, relaxed))
	goto error;
    updateSpatiaLiteHistory (sqlite, "*** ISO Metadata ***", NULL,
			     "ISO Metadata tables successfully created");
    sqlite3_result_int (context, 1);
    return;

  error:
    sqlite3_result_int (context, 0);
    return;
}

static void
fnct_GetIsoMetadataId (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ GetIsoMetadataId(String fileIdentifier)
/
/ return the ID of the row corresponding to "fileIdentifier"
/ 0 on failure / -1 on invalid argument
*/
    const char *fileIdentifier;
    sqlite3_int64 id;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    fileIdentifier = (const char *) sqlite3_value_text (argv[0]);
    if (!get_iso_metadata_id (sqlite, fileIdentifier, &id))
	sqlite3_result_int (context, 0);
    else
	sqlite3_result_int64 (context, id);
}

static void
fnct_RegisterIsoMetadata (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ RegisterIsoMetadata(String scope, BLOB metadata)
/  or
/ RegisterIsoMetadata(String scope, BLOB metadata, 
/			Integer id)
/  or
/ RegisterIsoMetadata(String scope, BLOB metadata, 
/			String fileIdentifier)
/
/ insert or updates a Raster Styled Layer 
/ returns 1 on success
/ 0 on failure, -1 on invalid arguments
*/
    int ret;
    const char *scope;
    const unsigned char *p_blob;
    int n_bytes;
    sqlite3_int64 id = -1;
    const char *fileIdentifier = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc == 3)
      {
	  /* optional extra args */
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER ||
	      sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      ;
	  else
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    scope = (const char *) sqlite3_value_text (argv[0]);
    p_blob = sqlite3_value_blob (argv[1]);
    n_bytes = sqlite3_value_bytes (argv[1]);
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      id = sqlite3_value_int64 (argv[2]);
	  if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	      fileIdentifier = (const char *) sqlite3_value_text (argv[2]);
      }
    ret = register_iso_metadata (sqlite, scope, p_blob, n_bytes, &id,
				 fileIdentifier);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_Create (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_Create(BLOB XMLdocument)
/ XB_Create(BLOB XMLdocument, bool compressed)
/ XB_Create(BLOB XMLdocument, bool compressed, text SchemaURI)
/ XB_Create(BLOB XMLdocument, bool compressed, int InternalSchemaURI)
/
/ returns the current XmlBlob by parsing an XMLdocument 
/ or NULL if any error is encountered
/
/ - the XMLdocument should be "well formed"
/ - if *compressed* is TRUE (default) the XmlBlob would be zipped
/ - if *SchemaURI* in not NULL then only XMLdocuments successfully
/   passing a formal Schema Validation will be accepted as valid
/ - if *InternalSchamaURI* is defined (any numeric value) then an
/   attempt will be made in order to identify a SchemaURI defined
/   internally within the XMLDocument itself.
/   if such internal SchemaURI doesn't exists, or if the formal
/   Schema Validation fails, NULL will be returned.
*/
    int len = 0;
    unsigned char *p_result = NULL;
    const unsigned char *xml;
    int xml_len;
    int compressed = 1;
    int use_internal_schema_uri = 0;
    const char *schemaURI = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc >= 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      use_internal_schema_uri = 1;
	  else if (sqlite3_value_type (argv[2]) != SQLITE_TEXT)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    xml = (const unsigned char *) sqlite3_value_blob (argv[0]);
    xml_len = sqlite3_value_bytes (argv[0]);
    if (argc >= 2)
	compressed = sqlite3_value_int (argv[1]);
    if (use_internal_schema_uri)
      {
	  /* using the SchemaURI internally defined within the XMLDocument */
	  char *internalSchemaURI =
	      gaiaXmlGetInternalSchemaURI (sqlite3_user_data (context), xml,
					   xml_len);
	  if (internalSchemaURI == NULL)
	    {
		/* unable to identify the SchemaURI */
		p_result = NULL;
	    }
	  else
	    {
		/* ok, attempting to validate using the internal SchemaURI */
		gaiaXmlToBlob (sqlite3_user_data (context), xml, xml_len,
			       compressed, internalSchemaURI, &p_result,
			       &len, NULL, NULL);
		free (internalSchemaURI);
	    }
      }
    else
      {
	  if (argc == 3)
	      schemaURI = (const char *) sqlite3_value_text (argv[2]);
	  gaiaXmlToBlob (sqlite3_user_data (context), xml, xml_len,
			 compressed, schemaURI, &p_result, &len, NULL, NULL);
      }
    if (p_result == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_XB_LoadXML (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_LoadXML(text path-or-URL)
/
/ returns a generic Text by parsing an XML Document 
/ or NULL if any error is encountered
/
*/
    const char *path_or_url;
    unsigned char *xml;
    int xml_len;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    path_or_url = (const char *) sqlite3_value_text (argv[0]);

/* acquiring the XML Document as a Blob */
    ret = gaiaXmlLoad
	(sqlite3_user_data (context), path_or_url, &xml, &xml_len, NULL);
    if (!ret || xml == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }

    sqlite3_result_blob (context, xml, xml_len, free);
}

static void
fnct_XB_GetPayload (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetPayload(XmlBLOB)
/ XB_GetPayload(XmlBLOB, int format)
/
/ returns the current XMLDocument (as BLOB) by parsing an XmlBLOB 
/ or NULL if any error is encountered
/
/ the returned buffer will be always null-terminated
*/
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *out;
    int out_len;
    int indent = -1;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc == 2)
	indent = sqlite3_value_int (argv[1]);
    gaiaXmlFromBlob (p_blob, n_bytes, indent, &out, &out_len);
    if (out == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_blob (context, out, out_len, free);
}

static void
fnct_XB_StoreXML (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_StoreXML(XmlBLOB, text path)
/ XB_StoreXML(XmlBLOB, taxt path, int format)
/
/ exports the current XMLDocument into an external file by parsing an XmlBLOB 
/ return 1 on success, 0 on failure, -1 on invalid args
/
*/
    const unsigned char *p_blob;
    int n_bytes;
    const char *path;
    int indent = -1;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_int (context, -1);
		return;
	    }
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    path = (const char *) sqlite3_value_text (argv[1]);
    if (argc == 3)
	indent = sqlite3_value_int (argv[2]);
    if (!gaiaXmlStore (p_blob, n_bytes, path, indent))
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_result_int (context, 1);
}

static void
fnct_XB_GetDocument (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetDocument(XmlBLOB)
/ XB_GetDocument(XmlBLOB, int indent)
/
/ returns the current XMLDocument (as UTF-8 TEXT) by parsing an XmlBLOB 
/ or NULL if any error is encountered
/
/ the returned buffer will be always null-terminated
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *xml;
    int len;
    int indent = -1;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 2)
      {
	  if (sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc == 2)
	indent = sqlite3_value_int (argv[1]);
    xml = gaiaXmlTextFromBlob (p_blob, n_bytes, indent);
    if (xml == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    len = strlen ((const char *) xml);
    sqlite3_result_text (context, (char *) xml, len, free);
}

static void
fnct_XB_SchemaValidate (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
/* SQL function:
/ XB_SchemaValidate(XmlBLOB, text SchemaURI)
/ XB_SchemaValidate(XmlBLOB, text SchemaURI, bool compressed)
/ XB_SchemaValidate(XmlBLOB, int InternalSchemaURI)
/ XB_SchemaValidate(XmlBLOB, int InternalSchemaURI, bool compressed)
/
/ returns a validated XmlBLOB object if the SchemaValidation was successful
/ or NULL if any error is encountered
*/
    int len = 0;
    unsigned char *p_result = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *xml;
    int xml_len;
    int compressed = 1;
    const char *schemaURI = NULL;
    int use_internal_schema_uri = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	use_internal_schema_uri = 1;
    else if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    if (argc == 3)
	compressed = sqlite3_value_int (argv[2]);
    gaiaXmlFromBlob (p_blob, n_bytes, -1, &xml, &xml_len);
    if (xml == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (use_internal_schema_uri)
      {
	  /* using the SchemaURI internally defined within the XMLDocument */
	  char *internalSchemaURI =
	      gaiaXmlGetInternalSchemaURI (sqlite3_user_data (context), xml,
					   xml_len);
	  if (internalSchemaURI == NULL)
	    {
		/* unable to identify the SchemaURI */
		p_result = NULL;
	    }
	  else
	    {
		/* ok, attempting to validate using the internal SchemaURI */
		gaiaXmlToBlob (sqlite3_user_data (context), xml, xml_len,
			       compressed, internalSchemaURI, &p_result,
			       &len, NULL, NULL);
		free (internalSchemaURI);
	    }
      }
    else
      {
	  schemaURI = (const char *) sqlite3_value_text (argv[1]);
	  gaiaXmlToBlob (sqlite3_user_data (context), xml, xml_len,
			 compressed, schemaURI, &p_result, &len, NULL, NULL);
      }
    free (xml);
    if (p_result == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_XB_Compress (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_Compress(XmlBLOB)
/
/ returns a compressed XmlBLOB object 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    gaiaXmlBlobCompression (p_blob, n_bytes, 1, &p_result, &len);
    if (p_result == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_XB_Uncompress (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_Uncompress(XmlBLOB)
/
/ returns an uncompressed XmlBLOB object 
/ or NULL if any error is encountered
*/
    int len;
    unsigned char *p_result = NULL;
    const unsigned char *p_blob;
    int n_bytes;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    gaiaXmlBlobCompression (p_blob, n_bytes, 0, &p_result, &len);
    if (p_result == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    sqlite3_result_blob (context, p_result, len, free);
}

static void
fnct_XB_IsValid (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsValid(XmlBLOB)
/
/ returns TRUE if the current BLOB is an XmlBLOB, FALSE if not 
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsValidXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsCompressed (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsCompressed(XmlBLOB)
/
/ returns TRUE if the current BLOB is a compressed XmlBLOB,
/ FALSE if it's a valid uncompressed XmlBLOB 
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsCompressedXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsSchemaValidated (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsSchemaValidated(XmlBLOB)
/
/ returns TRUE if the current BLOB is a Schema validated XmlBLOB,
/ FALSE if it's a valid but not validated XmlBLOB 
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsSchemaValidatedXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsIsoMetadata (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsIsoMetadata(XmlBLOB)
/
/ returns TRUE if the current BLOB is an ISO Metadata XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not an ISO Metadata
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsIsoMetadataXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsSldSeVectorStyle (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsSldSeVectorStyle(XmlBLOB)
/
/ returns TRUE if the current BLOB is an SLD/SE Vector Style XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not an SLD/SE Style
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsSldSeVectorStyleXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsSldSeRasterStyle (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsSldSeRasterStyle(XmlBLOB)
/
/ returns TRUE if the current BLOB is an SLD/SE Raster Style XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not an SLD/SE Style
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsSldSeRasterStyleXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsSldStyle (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsSldStyle(XmlBLOB)
/
/ returns TRUE if the current BLOB is an SLD Style XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not an SLD Style
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsSldStyleXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsSvg (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsSvg(XmlBLOB)
/
/ returns TRUE if the current BLOB is an SLD/SE Style XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not an SLD/SE Style
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsSvgXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_IsGpx (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsGpx(XmlBLOB)
/
/ returns TRUE if the current BLOB is a GPX document XmlBLOB,
/ FALSE if it's a valid XmlBLOB but not a GPX document
/ or -1 if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaIsGpxXmlBlob (p_blob, n_bytes);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_GetDocumentSize (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetDocumentSize(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB will return the XMLDocument size (in bytes)
/ or NULL if any error is encountered
*/
    unsigned char *p_blob;
    int n_bytes;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    ret = gaiaXmlBlobGetDocumentSize (p_blob, n_bytes);
    if (ret < 0)
	sqlite3_result_null (context);
    else
	sqlite3_result_int (context, ret);
}

static void
fnct_XB_GetSchemaURI (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetSchemaURI(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a SchemaURI then
/ the SchemaURI will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *schema_uri;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    schema_uri = gaiaXmlBlobGetSchemaURI (p_blob, n_bytes);
    if (schema_uri == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, schema_uri, strlen (schema_uri), free);
}

static void
fnct_XB_GetFileId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetFileId(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a FileIdentifier then
/ the FileIdentifier will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *file_identifier;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    file_identifier = gaiaXmlBlobGetFileId (p_blob, n_bytes);
    if (file_identifier == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, file_identifier,
			     strlen (file_identifier), free);
}

static void
fnct_XB_GetParentId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetParentId(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a ParentIdentifier then
/ the ParentIdentifier will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *parent_identifier;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    parent_identifier = gaiaXmlBlobGetParentId (p_blob, n_bytes);
    if (parent_identifier == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, parent_identifier,
			     strlen (parent_identifier), free);
}

static void
fnct_XB_SetFileId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_SetFileId(XmlBLOB, Text value)
/
/ if the BLOB is a valid XmlBLOB of the ISO-Metadata type
/ already containing a FileID then this function will 
/ return a new XmlBLOB containing the new FileID
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *new_blob;
    int new_bytes;
    const char *identifier;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    identifier = (const char *) sqlite3_value_text (argv[1]);
    if (!gaiaXmlBlobSetFileId
	(sqlite3_user_data (context), p_blob, n_bytes, identifier, &new_blob,
	 &new_bytes))
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, new_blob, new_bytes, free);
}

static void
fnct_XB_SetParentId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_SetParentId(XmlBLOB, Text value)
/
/ if the BLOB is a valid XmlBLOB of the ISO-Metadata type
/ already containing a ParentID then this function will 
/ return a new XmlBLOB containing the new ParentID
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *new_blob;
    int new_bytes;
    const char *identifier;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    identifier = (const char *) sqlite3_value_text (argv[1]);
    if (!gaiaXmlBlobSetParentId
	(sqlite3_user_data (context), p_blob, n_bytes, identifier, &new_blob,
	 &new_bytes))
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, new_blob, new_bytes, free);
}

static void
fnct_XB_AddFileId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_AddFileId(XmlBLOB, Text value, Text ns_id, Text uri_id, Text ns_charstr, Text uri_charstr)
/
/ if the BLOB is a valid XmlBLOB of the ISO-Metadata type
/ not containing a FileID then this function will 
/ return a new XmlBLOB containing the new FileID
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *new_blob;
    int new_bytes;
    const char *identifier;
    const char *ns_id = NULL;
    const char *uri_id = NULL;
    const char *ns_charstr = NULL;
    const char *uri_charstr = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[3]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[4]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[5]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    identifier = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	ns_id = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	uri_id = (const char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	ns_charstr = (const char *) sqlite3_value_text (argv[4]);
    if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
	uri_charstr = (const char *) sqlite3_value_text (argv[5]);
    if (!gaiaXmlBlobAddFileId
	(sqlite3_user_data (context), p_blob, n_bytes, identifier, ns_id,
	 uri_id, ns_charstr, uri_charstr, &new_blob, &new_bytes))
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, new_blob, new_bytes, free);
}

static void
fnct_XB_AddParentId (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_AddParentId(XmlBLOB, Text value, Text ns_id, Text uri_id, Text ns_charstr, Text url_charstr)
/
/ if the BLOB is a valid XmlBLOB of the ISO-Metadata type
/ not containing a ParentID then this function will 
/ return a new XmlBLOB containing the new ParentID
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    unsigned char *new_blob;
    int new_bytes;
    const char *identifier;
    const char *ns_id = NULL;
    const char *uri_id = NULL;
    const char *ns_charstr = NULL;
    const char *uri_charstr = NULL;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[2]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[3]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[4]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_TEXT
	|| sqlite3_value_type (argv[5]) == SQLITE_NULL)
	;
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    identifier = (const char *) sqlite3_value_text (argv[1]);
    if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
	ns_id = (const char *) sqlite3_value_text (argv[2]);
    if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
	uri_id = (const char *) sqlite3_value_text (argv[3]);
    if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
	ns_charstr = (const char *) sqlite3_value_text (argv[4]);
    if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
	uri_charstr = (const char *) sqlite3_value_text (argv[5]);
    if (!gaiaXmlBlobAddParentId
	(sqlite3_user_data (context), p_blob, n_bytes, identifier, ns_id,
	 uri_id, ns_charstr, uri_charstr, &new_blob, &new_bytes))
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, new_blob, new_bytes, free);
}

static void
fnct_XB_GetName (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetName(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a Name then
/ the Name will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *name;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    name = gaiaXmlBlobGetName (p_blob, n_bytes);
    if (name == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, name, strlen (name), free);
}

static void
fnct_XB_GetTitle (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetTitle(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a Title then
/ the Title will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *title;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    title = gaiaXmlBlobGetTitle (p_blob, n_bytes);
    if (title == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, title, strlen (title), free);
}

static void
fnct_XB_GetAbstract (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetAbstract(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing an Abstract then
/ the Abstract will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *abstract;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    abstract = gaiaXmlBlobGetAbstract (p_blob, n_bytes);
    if (abstract == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, abstract, strlen (abstract), free);
}

static void
fnct_XB_GetGeometry (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetGeometry(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB containing a Geometry then
/ the Geometry will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    int blob_len;
    unsigned char *blob;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    gaiaXmlBlobGetGeometry (p_blob, n_bytes, &blob, &blob_len);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_len, free);
}

static void
fnct_XB_MLineFromGPX (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ XB_MLineFromGPX(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB of the GPX type then
/ a MultiLinestring will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    int blob_len;
    unsigned char *blob = NULL;
    gaiaGeomCollPtr geom;
    int gpkg_mode = 0;
    int tiny_point = 0;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    geom = gaiaXmlBlobMLineFromGPX (p_blob, n_bytes, sqlite);
    if (geom != NULL)
      {
	  /* builds the BLOB geometry to be returned */
	  gaiaToSpatiaLiteBlobWkbEx2 (geom, &blob, &blob_len, gpkg_mode,
				      tiny_point);
	  sqlite3_result_blob (context, blob, blob_len, free);
	  gaiaFreeGeomColl (geom);
      }
    else
	sqlite3_result_null (context);
}

static void
fnct_XB_GetEncoding (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetEncoding(XmlBLOB)
/
/ if the BLOB is a valid XmlBLOB explicitly defining an encoding then
/ the charset name will be returned
/ return NULL on any other case
*/
    const unsigned char *p_blob;
    int n_bytes;
    char *encoding;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    p_blob = sqlite3_value_blob (argv[0]);
    n_bytes = sqlite3_value_bytes (argv[0]);
    encoding = gaiaXmlBlobGetEncoding (p_blob, n_bytes);
    if (encoding == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, encoding, strlen (encoding), free);
}

static void
fnct_XB_GetInternalSchemaURI (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetInternalSchemaURI(XmlDocument)
/
/ if the XMLDocument is valid and it contains an internally
/ defined SchemaURI then this SchemaURI will be returned
/ return NULL on any other case
*/
    const unsigned char *xml;
    int xml_len;
    char *schema_uri;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    xml = sqlite3_value_blob (argv[0]);
    xml_len = sqlite3_value_bytes (argv[0]);
    schema_uri =
	gaiaXmlGetInternalSchemaURI (sqlite3_user_data (context), xml, xml_len);
    if (schema_uri == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, schema_uri, strlen (schema_uri), free);
}

static void
fnct_XB_GetLastParseError (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetLastParseError()
/
/ return the most recent XML Parse error/warning (if any)
/ return NULL on any other case
*/
    char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    msg = gaiaXmlBlobGetLastParseError (sqlite3_user_data (context));
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_XB_GetLastValidateError (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetLastValidateError()
/
/ return the most recent XML Validate error/warning (if any)
/ return NULL on any other case
*/
    char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    msg = gaiaXmlBlobGetLastValidateError (sqlite3_user_data (context));
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_XB_IsValidXPathExpression (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ XB_IsValidXPathExpression(text XPathExpression)
/
/ returns TRUE if the current arg is a valid XPathExpression,
/ FALSE if it's not
/ or -1 if any error is encountered
*/
    int ret;
    const char *xpath;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    xpath = (const char *) sqlite3_value_text (argv[0]);
    ret = gaiaIsValidXPathExpression (sqlite3_user_data (context), xpath);
    sqlite3_result_int (context, ret);
}

static void
fnct_XB_GetLastXPathError (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
/* SQL function:
/ XB_GetLastXPathError()
/
/ return the most recent XML Validate error/warning (if any)
/ return NULL on any other case
*/
    char *msg;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    msg = gaiaXmlBlobGetLastXPathError (sqlite3_user_data (context));
    if (msg == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, msg, strlen (msg), SQLITE_STATIC);
}

static void
fnct_XB_CacheFlush (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ XB_CacheFlush()
/
/ resets the Internal XML Schema Cache to its initial empty state
/ 
/ returns TRUE on success
*/
    int i;
    struct splite_xmlSchema_cache_item *p_xmlSchema;
    struct splite_internal_cache *cache = sqlite3_user_data (context);

    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

    for (i = 0; i < MAX_XMLSCHEMA_CACHE; i++)
      {
	  /* freeing the XmlSchema cache */
	  p_xmlSchema = &(cache->xmlSchemaCache[i]);
	  splite_free_xml_schema_cache_item (p_xmlSchema);
      }
    sqlite3_result_int (context, 1);
}

#endif /* end including LIBXML2 */

static void
fnct_AffineTransformMatrix_Create (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Create() - identity transformation
/    or
/ ATM_Create(double a, double b, double d, double e,
/            double xoff, double yoff) - 2D
/    or
/ ATM_Create(double a, double b, double c, double d, 
/            double e, double f, double g, double h,
/            double i, double xoff, double yoff,
/            double zoff) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (argc == 6)
      {
	  /* 2D transform */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      b = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		b = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      d = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		d = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[4]);
	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[4]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[5]);
	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[5]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 12)
      {
	  /* 3D transform */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      b = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		b = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      c = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		c = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      d = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		d = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[4]);
	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[4]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
	      f = sqlite3_value_double (argv[5]);
	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[5]);
		f = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
	      g = sqlite3_value_double (argv[6]);
	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[6]);
		g = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
	      h = sqlite3_value_double (argv[7]);
	  else if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[7]);
		h = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[8]) == SQLITE_FLOAT)
	      i = sqlite3_value_double (argv[8]);
	  else if (sqlite3_value_type (argv[8]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[8]);
		i = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[9]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[6]);
	  else if (sqlite3_value_type (argv[9]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[9]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[10]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[10]);
	  else if (sqlite3_value_type (argv[10]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[10]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[11]) == SQLITE_FLOAT)
	      zoff = sqlite3_value_double (argv[11]);
	  else if (sqlite3_value_type (argv[11]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[11]);
		zoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_CreateTranslate (sqlite3_context * context,
					    int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ATM_CreateTranslate(double tx, double ty) - 2D
/    or
/ ATM_CreateTranslate(double tx, double ty, double tz) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (argc == 2)
      {
	  /* 2D translate */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 3)
      {
	  /* 3D translate */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      zoff = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		zoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_CreateScale (sqlite3_context * context, int argc,
					sqlite3_value ** argv)
{
/* SQL function:
/ ATM_CreateScale(double sx, double sy) - 2D
/    or
/ ATM_CreateScale(double sx, double sy, double sz - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (argc == 2)
      {
	  /* 2D scale */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 3)
      {
	  /* 3D scale */
	  if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[0]);
	  else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[0]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      i = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		i = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_CreateRotate (sqlite3_context * context, int argc,
					 sqlite3_value ** argv)
{
/* SQL function:
/ ATM_CreateRotate(double angleInDegrees) - 2D
/    or
/ ATM_CreateZRoll(double angeInDegrees) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    a = vcos;
    b = -vsin;
    d = vsin;
    e = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_CreateXRoll (sqlite3_context * context, int argc,
					sqlite3_value ** argv)
{
/* SQL function:
/ ATM_CreateXRoll(double angleInDegrees) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    e = vcos;
    f = -vsin;
    h = vsin;
    i = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_CreateYRoll (sqlite3_context * context, int argc,
					sqlite3_value ** argv)
{
/* SQL function:
/ ATM_CreateYRoll(double angleInDegrees) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[0]);
    else if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[0]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    a = vcos;
    c = vsin;
    g = -vsin;
    i = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create (a, b, c, d, e, f, g, h, i, xoff, yoff, zoff, &blob,
			&blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_Multiply (sqlite3_context * context, int argc,
				     sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Multiply(blob atmA, blob atmB)
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob1;
    int iblob1_sz;
    const unsigned char *iblob2;
    int iblob2_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob1 = sqlite3_value_blob (argv[0]);
	  iblob1_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
      {
	  iblob2 = sqlite3_value_blob (argv[1]);
	  iblob2_sz = sqlite3_value_bytes (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_multiply (iblob1, iblob1_sz, iblob2, iblob2_sz, &blob,
			  &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_Translate (sqlite3_context * context, int argc,
				      sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Translate(blob atm, double tx, double ty) - 2D
/    or
/ ATM_Translate(blob atm, double tx, double ty, double tz) - 3D
/
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob = NULL;
    int iblob_sz = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (argc == 3)
      {
	  /* 2D translate */
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
	    {
		iblob = sqlite3_value_blob (argv[0]);
		iblob_sz = sqlite3_value_bytes (argv[0]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 4)
      {
	  /* 3D translate */
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
	    {
		iblob = sqlite3_value_blob (argv[0]);
		iblob_sz = sqlite3_value_bytes (argv[0]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      xoff = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		xoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      yoff = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		yoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      zoff = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		zoff = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create_multiply (iblob, iblob_sz, a, b, c, d, e, f, g, h, i,
				 xoff, yoff, zoff, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_Scale (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Scale(blob atm, double sx, double sy) - 2D
/    or
/ ATM_Scale(blob atm, double sx, double sy, double sz) - 3D
/
/ will create a BLOB-encoded Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob = NULL;
    int iblob_sz = 0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (argc == 3)
      {
	  /* 2D scale */
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
	    {
		iblob = sqlite3_value_blob (argv[0]);
		iblob_sz = sqlite3_value_bytes (argv[0]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    else if (argc == 4)
      {
	  /* 3D scale */
	  if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
	    {
		iblob = sqlite3_value_blob (argv[0]);
		iblob_sz = sqlite3_value_bytes (argv[0]);
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	      a = sqlite3_value_double (argv[1]);
	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[1]);
		a = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	      e = sqlite3_value_double (argv[2]);
	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[2]);
		e = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
	  if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	      i = sqlite3_value_double (argv[3]);
	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
	    {
		int_value = sqlite3_value_int (argv[3]);
		i = int_value;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create_multiply (iblob, iblob_sz, a, b, c, d, e, f, g, h, i,
				 xoff, yoff, zoff, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_Rotate (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Rotate(blob atm, double angleInDegrees) - 2D
/    or
/ ATM_ZRoll(blob atm, double angleInDegrees) - 3D
/
/ will create a BLOB-encoded Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob;
    int iblob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    a = vcos;
    b = -vsin;
    d = vsin;
    e = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create_multiply (iblob, iblob_sz, a, b, c, d, e, f, g, h, i,
				 xoff, yoff, zoff, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_XRoll (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ ATM_XRoll(double angleInDegrees, blob atm) - 3D
/
/ will create a BLOB-encoded Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob;
    int iblob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    e = vcos;
    f = -vsin;
    h = vsin;
    i = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create_multiply (iblob, iblob_sz, a, b, c, d, e, f, g, h, i,
				 xoff, yoff, zoff, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_YRoll (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ ATM_YRoll(double angleInDegrees, blob atm) - 3D
/
/ will create a BLOB-encoded Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    double a = 1.0;
    double b = 0.0;
    double c = 0.0;
    double d = 0.0;
    double e = 1.0;
    double f = 0.0;
    double g = 0.0;
    double h = 0.0;
    double i = 1.0;
    double xoff = 0.0;
    double yoff = 0.0;
    double zoff = 0.0;
    int int_value;
    double angle;
    double coeff = .0174532925199432958;
    double rads;
    double vsin;
    double vcos;
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob;
    int iblob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	angle = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int_value = sqlite3_value_int (argv[1]);
	  angle = int_value;
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    rads = angle * coeff;
    vsin = sin (rads);
    vcos = cos (rads);
    a = vcos;
    c = vsin;
    g = -vsin;
    i = vcos;

/* creating the BLOB-encoded Affine Transform Matrix */
    gaia_matrix_create_multiply (iblob, iblob_sz, a, b, c, d, e, f, g, h, i,
				 xoff, yoff, zoff, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_Determinant (sqlite3_context * context, int argc,
					sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Determinant(blob atm)
/
/ will return the Determinant from a BLOB-encoded Affine Transform Matrix
/ or 0.0 on failure
*/
    const unsigned char *iblob;
    int iblob_sz;
    double det = 0.0;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_double (context, det);
	  return;
      }

/* evaluating the Affine Transform Matrix */
    det = gaia_matrix_determinant (iblob, iblob_sz);
    sqlite3_result_double (context, det);
}

static void
fnct_AffineTransformMatrix_IsInvertible (sqlite3_context * context, int argc,
					 sqlite3_value ** argv)
{
/* SQL function:
/ ATM_IsInvertible(blob atm)
/
/ will return TRUE if the a BLOB-encoded Affine Transform Matrix
/ is Invertible, FALSE if not
/ -1 on invalid arguments
*/
    const unsigned char *iblob;
    int iblob_sz;
    double det;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }

/* evaluating the Affine Transform Matrix */
    det = gaia_matrix_determinant (iblob, iblob_sz);
    if (det != 0.0)
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
}

static void
fnct_AffineTransformMatrix_Invert (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Invert(blob atm)
/
/ will create an Inverse BLOB-encoded Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob;
    int iblob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob = sqlite3_value_blob (argv[0]);
	  iblob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* creating the BLOB-encoded Affine Transform Matrix (Inverse) */
    gaia_matrix_invert (iblob, iblob_sz, &blob, &blob_sz);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_GeometryTransform (sqlite3_context * context,
					      int argc, sqlite3_value ** argv)
{
/* SQL function:
/ ATM_Transform(blob geom, blob atm [ , int srid] )
/
/ will create a BLOB-Geometry by applying to the input Geometry all
/ transformations specified by the Affine Transform Matrix
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob1;
    int iblob1_sz;
    const unsigned char *iblob2;
    int iblob2_sz;
    gaiaGeomCollPtr g1;
    gaiaGeomCollPtr g2;
    int srid = -9999;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob1 = sqlite3_value_blob (argv[0]);
	  iblob1_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
      {
	  iblob2 = sqlite3_value_blob (argv[1]);
	  iblob2_sz = sqlite3_value_bytes (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* attempting to parse the BLOB-Geometry */
    g1 = gaiaFromSpatiaLiteBlobWkbEx (iblob1, iblob1_sz, gpkg_mode,
				      gpkg_amphibious);
    if (g1 == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    g2 = gaia_matrix_transform_geometry (g1, iblob2, iblob2_sz);
    gaiaFreeGeomColl (g1);
    if (g2 == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (srid != -9999)
	g2->Srid = srid;
    gaiaToSpatiaLiteBlobWkbEx2 (g2, &blob, &blob_sz, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (g2);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_AffineTransformMatrix_IsValid (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
/* SQL function:
/ ATM_IsValid(BLOB matrix)
/
/ returns TRUE if the current BLOB is a valid BLOB-ATM, FALSE if not 
/ or -1 if any error is encountered
*/
    const unsigned char *blob;
    int blob_sz;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (const unsigned char *) sqlite3_value_blob (argv[0]);
	  blob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }

/* verifying the BLOB-Matrix */
    ret = gaia_matrix_is_valid (blob, blob_sz);
    if (ret)
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
}

static void
fnct_AffineTransformMatrix_AsText (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
/* SQL function:
/ ATM_AsText(BLOB matrix)
/
/ returns a textual representaion of the BLOB-Matrix
/ or NULL if any error is encountered
*/
    const unsigned char *blob;
    int blob_sz;
    char *text;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (const unsigned char *) sqlite3_value_blob (argv[0]);
	  blob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving the textual representation from the BLOB-Matrix */
    text = gaia_matrix_as_text (blob, blob_sz);
    if (text == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, text, strlen (text), sqlite3_free);
}

#ifdef ENABLE_GCP		/* only if ControlPoints enabled */

static int
get_control_point (gaiaGeomCollPtr geom, double *x, double *y, double *z,
		   int *has3d)
{
/* checking a Control Point */
    gaiaPointPtr pt;
    if (geom == NULL)
	return 0;
    if (geom->FirstLinestring != NULL || geom->FirstPolygon != NULL)
	return 0;
    pt = geom->FirstPoint;
    if (pt == NULL || pt != geom->LastPoint)
	return 0;
    if (geom->DimensionModel == GAIA_XY_Z
	|| geom->DimensionModel == GAIA_XY_Z_M)
      {
	  *has3d = 1;
	  *x = pt->X;
	  *y = pt->Y;
	  *z = pt->Z;
      }
    else
      {
	  *has3d = 0;
	  *x = pt->X;
	  *y = pt->Y;
      }
    return 1;
}

static int
check_control_point_dims (GaiaControlPointsPtr cp_handle, int has3d_0,
			  int has3d_1)
{
/* validating Control Point dimensions */
    struct gaia_control_points *cp = (struct gaia_control_points *) cp_handle;
    if (cp == NULL)
	return 0;
    if (has3d_0 != cp->has3d)
	return 0;
    if (has3d_1 != cp->has3d)
	return 0;
    return 1;
}

static void
fnct_GroundControlPoints_Compute_step (sqlite3_context * context,
				       int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GCP_Compute(BLOB point1, BLOB point2 [ , int order ] )
/
/ aggregate function - STEP
/
*/
    unsigned char *p_blob0;
    int n_bytes0;
    unsigned char *p_blob1;
    int n_bytes1;
    gaiaGeomCollPtr geom0 = NULL;
    gaiaGeomCollPtr geom1 = NULL;
    double x0;
    double y0;
    double z0;
    double x1;
    double y1;
    double z1;
    int has3d_0;
    int has3d_1;
    int order = 1;
    int tps = 0;
    GaiaControlPointsPtr *cp;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
      }
    if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) != SQLITE_BLOB)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) != SQLITE_INTEGER)
	    {
		sqlite3_result_null (context);
		return;
	    }
	  order = sqlite3_value_int (argv[2]);
	  if (order >= 1 && order <= 3)
	      ;			/* 1st, 2nd or 3rd polynomial order */
	  else if (order == 0)
	    {
		/* Thin Plate Splite */
		order = 1;
		tps = 1;
	    }
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }
    p_blob0 = (unsigned char *) sqlite3_value_blob (argv[0]);
    n_bytes0 = sqlite3_value_bytes (argv[0]);
    p_blob1 = (unsigned char *) sqlite3_value_blob (argv[1]);
    n_bytes1 = sqlite3_value_bytes (argv[1]);
    geom0 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob0, n_bytes0, gpkg_mode,
				     gpkg_amphibious);
    geom1 =
	gaiaFromSpatiaLiteBlobWkbEx (p_blob1, n_bytes1, gpkg_mode,
				     gpkg_amphibious);
    if (!get_control_point (geom0, &x0, &y0, &z0, &has3d_0))
	goto error;
    if (!get_control_point (geom1, &x1, &y1, &z1, &has3d_1))
	goto error;
    if (has3d_0 != has3d_1)
	goto error;
    cp = sqlite3_aggregate_context (context,
				    sizeof (struct gaia_control_points));
    if (*cp == NULL)
      {
	  /* this is the first row */
	  *cp = gaiaCreateControlPoints (1024, has3d_0, order, tps);
	  if (has3d_0)
	      gaiaAddControlPoint3D (*cp, x0, y0, z0, x1, y1, z1);
	  else
	      gaiaAddControlPoint2D (*cp, x0, y0, x1, y1);
      }
    else
      {
	  /* subsequent rows */
	  if (!check_control_point_dims (*cp, has3d_0, has3d_1))
	      goto error;
	  if (has3d_0)
	      gaiaAddControlPoint3D (*cp, x0, y0, z0, x1, y1, z1);
	  else
	      gaiaAddControlPoint2D (*cp, x0, y0, x1, y1);
      }
    gaiaFreeGeomColl (geom0);
    gaiaFreeGeomColl (geom1);
    return;

  error:
    if (geom0 != NULL)
	gaiaFreeGeomColl (geom0);
    if (geom1 != NULL)
	gaiaFreeGeomColl (geom1);
    sqlite3_result_null (context);
}

static void
fnct_GroundControlPoints_Compute_final (sqlite3_context * context)
{
/* SQL function:
/ GCP_Compute(BLOB point1, BLOB point2 [ , int order ] )
/
/ aggregate function - FINAL
/
*/
    unsigned char *blob = NULL;
    int blob_sz;
    int ret;
    GaiaControlPointsPtr *cp = sqlite3_aggregate_context (context, 0);
    if (cp == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    ret = gaiaCreatePolynomialCoeffs (*cp, &blob, &blob_sz);
    if (!ret)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
    gaiaFreeControlPoints (*cp);
}

static void
fnct_GroundControlPoints_GeometryTransform (sqlite3_context * context,
					    int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GCP_Transform(blob geom, blob gcp [ , int srid ] )
/
/ will create a BLOB-Geometry by applying to the input Geometry all
/ transformations specified by the Polynomial coefficients
/ 
/ returns a BLOB-ATM object or NULL on failure
*/
    unsigned char *blob;
    int blob_sz;
    const unsigned char *iblob1;
    int iblob1_sz;
    const unsigned char *iblob2;
    int iblob2_sz;
    gaiaGeomCollPtr g1;
    gaiaGeomCollPtr g2;
    int srid = -9999;
    int gpkg_amphibious = 0;
    int gpkg_mode = 0;
    int tiny_point = 0;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache != NULL)
      {
	  gpkg_amphibious = cache->gpkg_amphibious_mode;
	  gpkg_mode = cache->gpkg_mode;
	  tiny_point = cache->tinyPointEnabled;
      }

/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  iblob1 = sqlite3_value_blob (argv[0]);
	  iblob1_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
      {
	  iblob2 = sqlite3_value_blob (argv[1]);
	  iblob2_sz = sqlite3_value_bytes (argv[1]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (argc == 3)
      {
	  if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
	      srid = sqlite3_value_int (argv[2]);
	  else
	    {
		sqlite3_result_null (context);
		return;
	    }
      }

/* attempting to parse the BLOB-Geometry */
    g1 = gaiaFromSpatiaLiteBlobWkbEx (iblob1, iblob1_sz, gpkg_mode,
				      gpkg_amphibious);
    if (g1 == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    g2 = gaiaPolynomialTransformGeometry (g1, iblob2, iblob2_sz);
    gaiaFreeGeomColl (g1);
    if (g2 == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (srid != -9999)
	g2->Srid = srid;
    gaiaToSpatiaLiteBlobWkbEx2 (g2, &blob, &blob_sz, gpkg_mode, tiny_point);
    gaiaFreeGeomColl (g2);
    if (blob == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, blob, blob_sz, free);
}

static void
fnct_GroundControlPoints_IsValid (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
/* SQL function:
/ GCO_IsValid(BLOB polynomail)
/
/ returns TRUE if the current BLOB is a valid BLOB-GCP, FALSE if not 
/ or -1 if any error is encountered
*/
    const unsigned char *blob;
    int blob_sz;
    int ret;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (const unsigned char *) sqlite3_value_blob (argv[0]);
	  blob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }

/* verifying the BLOB-Polynomail */
    ret = gaiaPolynomialIsValid (blob, blob_sz);
    if (ret)
	sqlite3_result_int (context, 1);
    else
	sqlite3_result_int (context, 0);
}

static void
fnct_GroundControlPoints_AsText (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
/* SQL function:
/ GCP_AsText(BLOB polynomail)
/
/ returns a textual representaion of the BLOB-Polynomial
/ or NULL if any error is encountered
*/
    const unsigned char *blob;
    int blob_sz;
    char *text;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (const unsigned char *) sqlite3_value_blob (argv[0]);
	  blob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* retrieving the textual representation from the BLOB-Matrix */
    text = gaiaPolynomialAsText (blob, blob_sz);
    if (text == NULL)
	sqlite3_result_null (context);
    else
	sqlite3_result_text (context, text, strlen (text), sqlite3_free);
}

static void
fnct_GroundControlPoints_ToATM (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ GCP2ATM(BLOB polynomail)
/
/ returns a BLOB-Atm corresponding to a BLOB-Polynomial (first order only)
/ or NULL if any error is encountered
*/
    const unsigned char *blob;
    int blob_sz;
    unsigned char *oblob;
    int oblob_sz;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
/* validating the input args */
    if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
      {
	  blob = (const unsigned char *) sqlite3_value_blob (argv[0]);
	  blob_sz = sqlite3_value_bytes (argv[0]);
      }
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* converting Polynomial coefficients into an Affine Transformation matrix */
    if (!gaiaPolynomialToMatrix (blob, blob_sz, &oblob, &oblob_sz))
	sqlite3_result_null (context);
    else
	sqlite3_result_blob (context, oblob, oblob_sz, free);
}

#endif /* end including GCP */

#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */

static void
fnct_enableGpkgMode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ EnableGpkgMode ( void )
/
/ returns: nothing
*/
    sqlite3 *sqlite;
    int ret;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	return;
    sqlite = sqlite3_context_db_handle (context);
    ret = checkSpatialMetaData (sqlite);
    if (ret != 4)
	return;			/* not GeoPackage */
    cache->gpkg_mode = 1;
    cache->gpkg_amphibious_mode = 0;
}

static void
fnct_disableGpkgMode (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ DisableGpkgMode ( void )
/
/ returns: nothing
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	return;
    cache->gpkg_mode = 0;
}

static void
fnct_getGpkgMode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ GetGpkgMode ( void )
/
/ returns: TRUE or FALSE
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_result_int (context, cache->gpkg_mode);
}


static void
fnct_enableGpkgAmphibiousMode (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
/* SQL function:
/ EnableGpkgAmphibiousMode ( void )
/
/ returns: nothing
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	return;
    cache->gpkg_mode = 0;
    cache->gpkg_amphibious_mode = 1;
}

static void
fnct_disableGpkgAmphibiousMode (sqlite3_context * context, int argc,
				sqlite3_value ** argv)
{
/* SQL function:
/ DisableGpkgAmphibiousMode ( void )
/
/ returns: nothing
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	return;
    cache->gpkg_amphibious_mode = 0;
}

static void
fnct_getGpkgAmphibiousMode (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ GetGpkgAmphibiousMode ( void )
/
/ returns: TRUE or FALSE
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    sqlite3_result_int (context, cache->gpkg_amphibious_mode);
}

#endif /* end GPKG conditional */

static void
fnct_setDecimalPrecision (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ SetDecimalPrecision ( int precision )
/ a negative precision identifies the default setting
/
/ returns: nothing
*/
    int precision = -1;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
	return;
    if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
	precision = sqlite3_value_int (argv[0]);
    else
	return;
    if (precision < 0)
	precision = -1;
    else if (precision == 6)
	precision = -1;
    else if (precision > 18)
	precision = 18;
    cache->decimal_precision = precision;
}

static void
fnct_getDecimalPrecision (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
/* SQL function:
/ GetDecimalPrecision ( void )
/
/ returns: the currently set Decimal Precision
*/
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_int (context, -1);
	  return;
      }
    sqlite3_result_int (context, cache->decimal_precision);
}

static void
fnct_enableTinyPoint (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
/* SQL function:
/ EnableTinyPoint ( void )
/
/ returns: nothing
*/
    const void *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    enable_tiny_point (cache);
}

static void
fnct_disableTinyPoint (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
/* SQL function:
/ DisableTinyPoint ( void )
/
/ returns: nothing
*/
    const void *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    disable_tiny_point (cache);
}

static void
fnct_isTinyPointEnabled (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ IsTinyPointEnabled ( void )
/
/ returns: TRUE or FALSE
*/
    int enabled;
    const void *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    enabled = is_tiny_point_enabled (cache);
    sqlite3_result_int (context, enabled);
}

static void
fnct_addShapefileExtent (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ AddShapefileExtent ( table Text, minx Double miny Double, maxx Double,
/                      maxy Double, srid Integer )
/
/ returns: 1 on success, 0 on failure
*/
    const char *table;
    double minx;
    double miny;
    double maxx;
    double maxy;
    int srid;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	table = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
	minx = sqlite3_value_double (argv[1]);
    else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[1]);
	  minx = val;
      }
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
	miny = sqlite3_value_double (argv[2]);
    else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[2]);
	  miny = val;
      }
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
	maxx = sqlite3_value_double (argv[3]);
    else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[3]);
	  maxx = val;
      }
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[4]) == SQLITE_FLOAT)
	maxy = sqlite3_value_double (argv[4]);
    else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
      {
	  int val = sqlite3_value_int (argv[4]);
	  maxy = val;
      }
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
	srid = sqlite3_value_int (argv[5]);
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    add_shp_extent (table, minx, miny, maxx, maxy, srid, cache);
    sqlite3_result_int (context, 1);
}

static void
fnct_removeShapefileExtent (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
/* SQL function:
/ RemoveShapefileExtent ( table Text )
/
/ returns: 1 on success, 0 on failure
*/
    const char *table;
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	table = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, 0);
	  return;
      }
    remove_shp_extent (table, cache);
    sqlite3_result_int (context, 1);
}

static void
fnct_getShapefileExtent (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
/* SQL function:
/ GetShapefileExtent ( table Text )
/
/ returns: the Shapefile's Full Extent (Envelope)
/          or NULL on error
*/
    const char *table;
    double minx;
    double miny;
    double maxx;
    double maxy;
    int srid;
    gaiaGeomCollPtr bbox;
    gaiaPolygonPtr polyg;
    gaiaRingPtr rect;
    char *sql;
    char *xtable;
    int len;
    unsigned char *p_result = NULL;
    sqlite3 *sqlite = sqlite3_context_db_handle (context);
    struct splite_internal_cache *cache = sqlite3_user_data (context);
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (cache == NULL)
      {
	  sqlite3_result_null (context);
	  return;
      }
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	table = (const char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_null (context);
	  return;
      }

/* ensuring to initialize the VirtualShape Table */
    xtable = gaiaDoubleQuotedSql (table);
    sql = sqlite3_mprintf ("PRAGMA table_info(\"%s\")", xtable);
    free (xtable);
    sqlite3_exec (sqlite, sql, NULL, NULL, NULL);
    sqlite3_free (sql);

    if (!get_shp_extent (table, &minx, &miny, &maxx, &maxy, &srid, cache))
      {
	  sqlite3_result_null (context);
	  return;
      }

/* building the Envelope */
    bbox = gaiaAllocGeomColl ();
    bbox->Srid = srid;
    polyg = gaiaAddPolygonToGeomColl (bbox, 5, 0);
    rect = polyg->Exterior;
    gaiaSetPoint (rect->Coords, 0, minx, miny);	/* vertex # 1 */
    gaiaSetPoint (rect->Coords, 1, maxx, miny);	/* vertex # 2 */
    gaiaSetPoint (rect->Coords, 2, maxx, maxy);	/* vertex # 3 */
    gaiaSetPoint (rect->Coords, 3, minx, maxy);	/* vertex # 4 */
    gaiaSetPoint (rect->Coords, 4, minx, miny);	/* vertex # 5 [same as vertex # 1 to close the polygon] */
/* builds the BLOB geometry to be returned */
    gaiaToSpatiaLiteBlobWkb (bbox, &p_result, &len);
    sqlite3_result_blob (context, p_result, len, free);
    gaiaFreeGeomColl (bbox);
}

static void
fnct_isLowASCII (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
/* SQL function:
/ IsLowASCII ( string Text )
/
/ returns: 1 (TRUE) is the argument is a text string containing
/          all low-ASCII chars (7-bit, < 128)
/          0 if not
/          -1 on invalid arg
*/
    int len;
    int i;
    const unsigned char *string;
    int result = 1;
    GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
    if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
	string = (const unsigned char *) sqlite3_value_text (argv[0]);
    else
      {
	  sqlite3_result_int (context, -1);
	  return;
      }

    len = strlen ((const char *) string);
    for (i = 0; i < len; i++)
      {
	  if (string[i] >= 128)
	      result = 0;
      }
    sqlite3_result_int (context, result);
}

#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */

static void
fnct_CreateTopoTables (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
    fnctaux_CreateTopoTables (context, argc, argv);
}

static void
fnct_CreateTopology (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_CreateTopology (context, argc, argv);
}

static void
fnct_DropTopology (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_DropTopology (context, argc, argv);
}

static void
fnct_GetLastTopologyException (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
    fnctaux_GetLastTopologyException (context, argc, argv);
}

static void
fnct_AddIsoNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_AddIsoNode (context, argc, argv);
}

static void
fnct_MoveIsoNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_MoveIsoNode (context, argc, argv);
}

static void
fnct_RemIsoNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemIsoNode (context, argc, argv);
}

static void
fnct_AddIsoEdge (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_AddIsoEdge (context, argc, argv);
}

static void
fnct_RemIsoEdge (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemIsoEdge (context, argc, argv);
}

static void
fnct_ModEdgeSplit (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_ModEdgeSplit (context, argc, argv);
}

static void
fnct_NewEdgesSplit (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_NewEdgesSplit (context, argc, argv);
}

static void
fnct_AddEdgeModFace (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_AddEdgeModFace (context, argc, argv);
}

static void
fnct_AddEdgeNewFaces (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_AddEdgeNewFaces (context, argc, argv);
}

static void
fnct_ChangeEdgeGeom (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_ChangeEdgeGeom (context, argc, argv);
}

static void
fnct_RemEdgeModFace (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemEdgeModFace (context, argc, argv);
}

static void
fnct_RemEdgeNewFace (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemEdgeNewFace (context, argc, argv);
}

static void
fnct_ModEdgeHeal (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_ModEdgeHeal (context, argc, argv);
}

static void
fnct_NewEdgeHeal (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_NewEdgeHeal (context, argc, argv);
}

static void
fnct_GetFaceGeometry (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_GetFaceGeometry (context, argc, argv);
}

static void
fnct_GetFaceEdges (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_GetFaceEdges (context, argc, argv);
}

static void
fnct_ValidateTopoGeo (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_ValidateTopoGeo (context, argc, argv);
}

static void
fnct_CreateTopoGeo (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_CreateTopoGeo (context, argc, argv);
}

static void
fnct_GetNodeByPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_GetNodeByPoint (context, argc, argv);
}

static void
fnct_GetEdgeByPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_GetEdgeByPoint (context, argc, argv);
}

static void
fnct_GetFaceByPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_GetFaceByPoint (context, argc, argv);
}

static void
fnct_TopoGeo_AddPoint (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
    fnctaux_TopoGeo_AddPoint (context, argc, argv);
}

static void
fnct_TopoGeo_AddLineString (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_AddLineString (context, argc, argv);
}

static void
fnct_TopoGeo_AddLineStringNoFace (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_AddLineStringNoFace (context, argc, argv);
}

static void
fnct_TopoGeo_FromGeoTable (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
    fnctaux_TopoGeo_FromGeoTable (context, argc, argv);
}

static void
fnct_TopoGeo_FromGeoTableNoFace (sqlite3_context * context, int argc,
				 sqlite3_value ** argv)
{
    fnctaux_TopoGeo_FromGeoTableNoFace (context, argc, argv);
}

static void
fnct_TopoGeo_FromGeoTableExt (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_FromGeoTableExt (context, argc, argv);
}

static void
fnct_TopoGeo_FromGeoTableNoFaceExt (sqlite3_context * context, int argc,
				    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_FromGeoTableNoFaceExt (context, argc, argv);
}

static void
fnct_TopoGeo_Polygonize (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
    fnctaux_TopoGeo_Polygonize (context, argc, argv);
}

static void
fnct_TopoGeo_TopoSnap (sqlite3_context * context, int argc,
		       sqlite3_value ** argv)
{
    fnctaux_TopoGeo_TopoSnap (context, argc, argv);
}

static void
fnct_TopoGeo_SnappedGeoTable (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_SnappedGeoTable (context, argc, argv);
}

static void
fnct_TopoGeo_ToGeoTable (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
    fnctaux_TopoGeo_ToGeoTable (context, argc, argv);
}

static void
fnct_TopoGeo_PolyFacesList (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_PolyFacesList (context, argc, argv);
}

static void
fnct_TopoGeo_LineEdgesList (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_LineEdgesList (context, argc, argv);
}

static void
fnct_TopoGeo_ToGeoTableGeneralize (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
    fnctaux_TopoGeo_ToGeoTableGeneralize (context, argc, argv);
}

static void
fnct_TopoGeo_RemoveSmallFaces (sqlite3_context * context, int argc,
			       sqlite3_value ** argv)
{
    fnctaux_TopoGeo_RemoveSmallFaces (context, argc, argv);
}

static void
fnct_TopoGeo_RemoveDanglingEdges (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_RemoveDanglingEdges (context, argc, argv);
}

static void
fnct_TopoGeo_RemoveDanglingNodes (sqlite3_context * context, int argc,
				  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_RemoveDanglingNodes (context, argc, argv);
}

static void
fnct_TopoGeo_NewEdgeHeal (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_NewEdgeHeal (context, argc, argv);
}

static void
fnct_TopoGeo_ModEdgeHeal (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_ModEdgeHeal (context, argc, argv);
}

static void
fnct_TopoGeo_NewEdgesSplit (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_NewEdgesSplit (context, argc, argv);
}

static void
fnct_TopoGeo_ModEdgeSplit (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
    fnctaux_TopoGeo_ModEdgeSplit (context, argc, argv);
}

static void
fnct_TopoGeo_CreateTopoLayer (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_CreateTopoLayer (context, argc, argv);
}

static void
fnct_TopoGeo_InitTopoLayer (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoGeo_InitTopoLayer (context, argc, argv);
}

static void
fnct_TopoGeo_RemoveTopoLayer (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_RemoveTopoLayer (context, argc, argv);
}

static void
fnct_TopoGeo_ExportTopoLayer (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_ExportTopoLayer (context, argc, argv);
}

static void
fnct_TopoGeo_InsertFeatureFromTopoLayer (sqlite3_context * context, int argc,
					 sqlite3_value ** argv)
{
    fnctaux_TopoGeo_InsertFeatureFromTopoLayer (context, argc, argv);
}

static void
fnct_TopoGeo_Clone (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_TopoGeo_Clone (context, argc, argv);
}

static void
fnct_TopoGeo_SubdivideLines (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
    fnctaux_TopoGeo_SubdivideLines (context, argc, argv);
}

static void
fnct_TopoGeo_DisambiguateSegmentEdges (sqlite3_context * context, int argc,
				       sqlite3_value ** argv)
{
    fnctaux_TopoGeo_DisambiguateSegmentEdges (context, argc, argv);
}

static void
fnct_TopoGeo_GetEdgeSeed (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_GetEdgeSeed (context, argc, argv);
}

static void
fnct_TopoGeo_GetFaceSeed (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_GetFaceSeed (context, argc, argv);
}

static void
fnct_TopoGeo_UpdateSeeds (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoGeo_UpdateSeeds (context, argc, argv);
}

static void
fnct_TopoGeo_SnapPointToSeed (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_TopoGeo_SnapPointToSeed (context, argc, argv);
}

static void
fnct_TopoGeo_SnapLineToSeed (sqlite3_context * context, int argc,
			     sqlite3_value ** argv)
{
    fnctaux_TopoGeo_SnapLineToSeed (context, argc, argv);
}

static void
fnct_CreateNetwork (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_CreateNetwork (context, argc, argv);
}

static void
fnct_DropNetwork (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_DropNetwork (context, argc, argv);
}

static void
fnct_GetLastNetworkException (sqlite3_context * context, int argc,
			      sqlite3_value ** argv)
{
    fnctaux_GetLastNetworkException (context, argc, argv);
}

static void
fnct_AddIsoNetNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_AddIsoNetNode (context, argc, argv);
}

static void
fnct_MoveIsoNetNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_MoveIsoNetNode (context, argc, argv);
}

static void
fnct_RemIsoNetNode (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemIsoNetNode (context, argc, argv);
}

static void
fnct_AddLink (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_AddLink (context, argc, argv);
}

static void
fnct_ChangeLinkGeom (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_ChangeLinkGeom (context, argc, argv);
}

static void
fnct_RemoveLink (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_RemoveLink (context, argc, argv);
}

static void
fnct_NewLogLinkSplit (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_NewLogLinkSplit (context, argc, argv);
}

static void
fnct_ModLogLinkSplit (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_ModLogLinkSplit (context, argc, argv);
}

static void
fnct_NewGeoLinkSplit (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_NewGeoLinkSplit (context, argc, argv);
}

static void
fnct_ModGeoLinkSplit (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_ModGeoLinkSplit (context, argc, argv);
}

static void
fnct_ModLinkHeal (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_ModLinkHeal (context, argc, argv);
}

static void
fnct_NewLinkHeal (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_NewLinkHeal (context, argc, argv);
}

static void
fnct_LogiNetFromTGeo (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_LogiNetFromTGeo (context, argc, argv);
}

static void
fnct_SpatNetFromTGeo (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_SpatNetFromTGeo (context, argc, argv);
}

static void
fnct_SpatNetFromGeom (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_SpatNetFromGeom (context, argc, argv);
}

static void
fnct_ValidLogicalNet (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_ValidLogicalNet (context, argc, argv);
}

static void
fnct_ValidSpatialNet (sqlite3_context * context, int argc,
		      sqlite3_value ** argv)
{
    fnctaux_ValidSpatialNet (context, argc, argv);
}

static void
fnct_GetNetNodeByPoint (sqlite3_context * context, int argc,
			sqlite3_value ** argv)
{
    fnctaux_GetNetNodeByPoint (context, argc, argv);
}

static void
fnct_GetLinkByPoint (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_GetLinkByPoint (context, argc, argv);
}

static void
fnct_TopoNet_FromGeoTable (sqlite3_context * context, int argc,
			   sqlite3_value ** argv)
{
    fnctaux_TopoNet_FromGeoTable (context, argc, argv);
}

static void
fnct_TopoNet_ToGeoTable (sqlite3_context * context, int argc,
			 sqlite3_value ** argv)
{
    fnctaux_TopoNet_ToGeoTable (context, argc, argv);
}

static void
fnct_TopoNet_ToGeoTableGeneralize (sqlite3_context * context, int argc,
				   sqlite3_value ** argv)
{
    fnctaux_TopoNet_ToGeoTableGeneralize (context, argc, argv);
}

static void
fnct_TopoNet_Clone (sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    fnctaux_TopoNet_Clone (context, argc, argv);
}

static void
fnct_TopoNet_GetLinkSeed (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoNet_GetLinkSeed (context, argc, argv);
}

static void
fnct_TopoNet_UpdateSeeds (sqlite3_context * context, int argc,
			  sqlite3_value ** argv)
{
    fnctaux_TopoNet_UpdateSeeds (context, argc, argv);
}

static void
fnct_TopoNet_DisambiguateSegmentLinks (sqlite3_context * context, int argc,
				       sqlite3_value ** argv)
{
    fnctaux_TopoNet_DisambiguateSegmentLinks (context, argc, argv);
}

static void
fnct_TopoNet_LineLinksList (sqlite3_context * context, int argc,
			    sqlite3_value ** argv)
{
    fnctaux_TopoNet_LineLinksList (context, argc, argv);
}

#endif /* end TOPOLOGY conditionals */

#ifdef LOADABLE_EXTENSION
static void
splite_close_callback (void *p_cache)
{
/*
/ the DB connection has been terminated 
/
/ this callback function is expected to be invoked only if 
/ SpatiaLite was loaded as a dynamic extension and will
/ perform a final clean-up releasing the internal cache
/
*/
    struct splite_internal_cache *cache =
	(struct splite_internal_cache *) p_cache;

    if (cache == NULL)
	return;
    if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
	return;

#ifdef ENABLE_RTTOPO
    gaiaResetRtTopoMsg (cache);
#endif

    free_internal_cache (cache);
}
#endif

SPATIALITE_PRIVATE void *
register_spatialite_sql_functions (void *p_db, const void *p_cache)
{
    sqlite3 *db = p_db;
    struct splite_internal_cache *cache =
	(struct splite_internal_cache *) p_cache;
    const char *security_level;

#ifdef LOADABLE_EXTENSION
/* registering the CLOSE-CALLBACK function */
    sqlite3_create_function_v2 (db, "spatialite_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_spatialite_version, 0, 0,
				splite_close_callback);
#else
    sqlite3_create_function_v2 (db, "spatialite_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_spatialite_version, 0, 0, 0);
#endif

    sqlite3_create_function_v2 (db, "spatialite_target_cpu", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_spatialite_target_cpu, 0, 0, 0);
    sqlite3_create_function_v2 (db, "freexl_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_freexl_version, 0, 0, 0);
    sqlite3_create_function_v2 (db, "proj4_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_proj4_version, 0, 0, 0);
    sqlite3_create_function_v2 (db, "geos_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_geos_version, 0, 0, 0);
    sqlite3_create_function_v2 (db, "rttopo_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_rttopo_version, 0, 0, 0);
    sqlite3_create_function_v2 (db, "libxml2_version", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_libxml2_version, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasProj", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_proj, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasProjGeodesic", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_proj_geodesic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeos", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geos, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeosAdvanced", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geos_advanced, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeosTrunk", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geos_trunk, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeosReentrant", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geos_reentrant, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeosOnlyReentrant", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geos_only_reentrant, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasRtTopo", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_rttopo, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasMathSql", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_math_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeoCallbacks", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geo_callbacks, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasIconv", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_iconv, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasFreeXL", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_freeXL, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasEpsg", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_epsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasLibXML2", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_libxml2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGeoPackage", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_geopackage, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGCP", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_gcp, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasGroundControlPoints", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_gcp, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasTopology", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_topology, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasKNN", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_knn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HasRouting", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_has_routing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryConstraints", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GeometryConstraints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryConstraints", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GeometryConstraints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RTreeAlign", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RTreeAlign, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidFont", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidFont, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckFontFaceName", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckFontFacename, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetFontFamily", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GetFontFamily, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsFontBold", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsFontBold, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsFontItalic", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsFontItalic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidPixel", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidPixel, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidRasterPalette", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidRasterPalette, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidRasterStatistics", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidRasterStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidRasterTile", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidRasterTile, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsPopulatedCoverage", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsPopulatedCoverage, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckSpatialMetaData", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckSpatialMetaData", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckGeoPackageMetaData", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckGeoPackageMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckGeoPackageMetaData", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckGeoPackageMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoFDOStart", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoFDOStart, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoFDOStart", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoFDOStart, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoFDOStop", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoFDOStop, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoFDOStop", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoFDOStop, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitFDOSpatialMetaData", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitFDOSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddFDOGeometryColumn", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AddFDOGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverFDOGeometryColumn", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverFDOGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DiscardFDOGeometryColumn", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DiscardFDOGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaData", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaData", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaData", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaData, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaDataFull", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaDataFull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaDataFull", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaDataFull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InitSpatialMetaDataFull", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InitSpatialMetaDataFull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InsertEpsgSrid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InsertEpsgSrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridIsGeographic", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridIsGeographic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridIsProjected", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridIsProjected, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridHasFlippedAxes", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridHasFlippedAxes, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetSpheroid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetSpheroid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetEllipsoid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetSpheroid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetPrimeMeridian", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetPrimeMeridian, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetDatum", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetDatum, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetProjection", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetProjection, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetUnit", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetUnit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetAxis_1_Name", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetAxis1Name, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetAxis_1_Orientation", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetAxis1Orientation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetAxis_2_Name", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetAxis2Name, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridGetAxis_2_Orientation", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridGetAxis2Orientation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddGeometryColumn", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AddGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddGeometryColumn", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AddGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddGeometryColumn", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AddGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverGeometryColumn", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverGeometryColumn", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpgradeGeometryTriggers", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpgradeGeometryTriggers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DiscardGeometryColumn", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DiscardGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RegisterVirtualGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterVirtualGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DropVirtualGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DropVirtualGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverSpatialIndex", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverSpatialIndex", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverSpatialIndex", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RecoverSpatialIndex", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RecoverSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckSpatialIndex", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckSpatialIndex", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckShadowedRowid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckShadowedRowid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckWithoutRowid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckWithoutRowid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateSpatialIndex", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateMbrCache", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateMbrCache, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DisableSpatialIndex", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DisableSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RebuildGeometryTriggers", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RebuildGeometryTriggers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpdateLayerStatistics", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpdateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpdateLayerStatistics", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpdateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpdateLayerStatistics", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpdateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLayerExtent", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLayerExtent, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLayerExtent", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLayerExtent, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLayerExtent", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLayerExtent, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InvalidateLayerStatistics", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InvalidateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InvalidateLayerStatistics", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InvalidateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InvalidateLayerStatistics", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_InvalidateLayerStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRasterCoveragesTable", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateRasterCoveragesTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateVectorCoveragesTables", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateVectorCoveragesTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "WMS_CreateTables", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateWMSTables, 0, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterGetCapabilities", 1, SQLITE_ANY,
			     0, fnct_RegisterWMSGetCapabilities, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterGetCapabilities", 3, SQLITE_ANY,
			     0, fnct_RegisterWMSGetCapabilities, 0, 0);
    sqlite3_create_function (db, "WMS_UnRegisterGetCapabilities", 1,
			     SQLITE_ANY, 0, fnct_UnregisterWMSGetCapabilities,
			     0, 0);
    sqlite3_create_function (db, "WMS_SetGetCapabilitiesInfos", 3, SQLITE_ANY,
			     0, fnct_SetWMSGetCapabilitiesInfos, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterGetMap", 9, SQLITE_ANY, 0,
			     fnct_RegisterWMSGetMap, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterGetMap", 13, SQLITE_ANY, 0,
			     fnct_RegisterWMSGetMap, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterGetMap", 18, SQLITE_ANY, 0,
			     fnct_RegisterWMSGetMap, 0, 0);
    sqlite3_create_function (db, "WMS_UnRegisterGetMap", 2, SQLITE_ANY, 0,
			     fnct_UnregisterWMSGetMap, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapInfos", 4, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapInfos, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapCopyright", 3, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapCopyright, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapCopyright", 4, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapCopyright, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapOptions", 3, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapOptions, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapOptions", 4, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapOptions, 0, 0);
    sqlite3_create_function (db, "WMS_SetGetMapOptions", 6, SQLITE_ANY, 0,
			     fnct_SetWMSGetMapOptions, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterSetting", 4, SQLITE_ANY, 0,
			     fnct_RegisterWMSSetting, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterSetting", 5, SQLITE_ANY, 0,
			     fnct_RegisterWMSSetting, 0, 0);
    sqlite3_create_function (db, "WMS_DefaultSetting", 4, SQLITE_ANY, 0,
			     fnct_DefaultWMSSetting, 0, 0);
    sqlite3_create_function (db, "WMS_UnRegisterSetting", 4, SQLITE_ANY, 0,
			     fnct_UnregisterWMSSetting, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterRefSys", 7, SQLITE_ANY, 0,
			     fnct_RegisterWMSRefSys, 0, 0);
    sqlite3_create_function (db, "WMS_RegisterRefSys", 8, SQLITE_ANY, 0,
			     fnct_RegisterWMSRefSys, 0, 0);
    sqlite3_create_function (db, "WMS_DefaultRefSys", 3, SQLITE_ANY, 0,
			     fnct_DefaultWMSRefSys, 0, 0);
    sqlite3_create_function (db, "WMS_UnRegisterRefSys", 3, SQLITE_ANY, 0,
			     fnct_UnregisterWMSRefSys, 0, 0);
    sqlite3_create_function (db, "WMS_GetMapRequestURL", 8, SQLITE_ANY, 0,
			     fnct_WMSGetMapRequestURL, 0, 0);
    sqlite3_create_function (db, "WMS_GetFeatureInfoRequestURL", 10,
			     SQLITE_ANY, 0, fnct_WMSGetFeatureInfoRequestURL,
			     0, 0);
    sqlite3_create_function (db, "WMS_GetFeatureInfoRequestURL", 11,
			     SQLITE_ANY, 0, fnct_WMSGetFeatureInfoRequestURL,
			     0, 0);
    sqlite3_create_function (db, "RegisterDataLicense", 1, SQLITE_ANY, 0,
			     fnct_RegisterDataLicense, 0, 0);
    sqlite3_create_function (db, "RegisterDataLicense", 2, SQLITE_ANY, 0,
			     fnct_RegisterDataLicense, 0, 0);
    sqlite3_create_function (db, "UnRegisterDataLicense", 1, SQLITE_ANY, 0,
			     fnct_UnRegisterDataLicense, 0, 0);
    sqlite3_create_function (db, "RenameDataLicense", 2, SQLITE_ANY, 0,
			     fnct_RenameDataLicense, 0, 0);
    sqlite3_create_function (db, "SetDataLicenseUrl", 2, SQLITE_ANY, 0,
			     fnct_SetDataLicenseUrl, 0, 0);
    sqlite3_create_function_v2 (db, "CreateMetaCatalogTables", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateMetaCatalogTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpdateMetaCatalogStatistics", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpdateMetaCatalogStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UpdateMetaCatalogStatistics", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UpdateMetaCatalogStatistics, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsWkt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsWkt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsWkt", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsWkt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsSvg", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsSvg1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsSvg", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsSvg2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsSvg", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsSvg3, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 9,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 10,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 11,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 12,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 13,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CloneTable", 14,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CloneTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 9,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 10,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 11,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 12,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 13,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateClonedTable", 14,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateClonedTable, 0, 0, 0);

#ifndef OMIT_PROJ		/* PROJ.4 is strictly required to support KML */
    sqlite3_create_function_v2 (db, "AsKml", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsKml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsKml", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsKml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsKml", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsKml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsKml", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsKml, 0, 0, 0);
#endif /* end including PROJ.4 */

    sqlite3_create_function_v2 (db, "AsGml", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGml", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGml", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromGml", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromGml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGeoJSON", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGeoJSON, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGeoJSON", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGeoJSON, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGeoJSON", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsGeoJSON, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromGeoJSON", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromGeoJSON, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromKml", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromKml, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsFGF", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsFGF, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromEWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromEWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsEWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToEWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsEWKT", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToEWKT, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromEWKT", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromEWKT, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsBinary", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsBinary, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsBinary", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsBinary, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomCollFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomCollFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryCollectionFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryCollectionFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineStringFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineStringFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolygonFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolygonFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MLineFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MLineFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiLineStringFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiLineStringFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPolygonFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPolygonFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomCollFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomCollFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryCollectionFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryCollectionFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineStringFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_LineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineStringFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolygonFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PolygonFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MLineFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MLineFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiLineStringFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiLineStringFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPolygonFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MultiPolygonFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_WKTToSQL", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_WktToSql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomCollFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomCollFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryCollectionFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryCollectionFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineStringFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineStringFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolygonFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolygonFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPointFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPointFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MLineFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MLineFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiLineStringFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiLineStringFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPolygonFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPolygonFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_WKBToSQL", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_WkbToSql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomCollFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeomCollFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryCollectionFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryCollectionFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeomCollFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineStringFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineStringFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolygonFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PolygonFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPointFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPointFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPointFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MLineFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MLineFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiLineStringFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiLineStringFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MLineFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPolygonFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MultiPolygonFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MPolyFromWkb2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromFGF", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryFromFGF1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromFGF", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryFromFGF2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CompressGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CompressGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UncompressGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_UncompressGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SanitizeGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SanitizeGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "EnsureClosedRings", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_EnsureClosedRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RemoveRepeatedPoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemoveRepeatedPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RemoveRepeatedPoints", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemoveRepeatedPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToInteger", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToInteger, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToDouble", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToDouble, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToBlob", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CastToBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ForceAsNull", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ForceAsNull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateUUID", 0, SQLITE_UTF8, 0,
				fnct_CreateUUID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MD5Checksum", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MD5Checksum, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MD5TotalChecksum", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_MD5TotalChecksum_step,
				fnct_MD5TotalChecksum_final, 0);

#if OMIT_ICONV == 0		/* ICONV is absolutely required */

    sqlite3_create_function_v2 (db, "EncodeURL", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_EncodeURL, 0, 0, 0);
    sqlite3_create_function_v2 (db, "EncodeURL", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_EncodeURL, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DecodeURL", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DecodeURL, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DecodeURL", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DecodeURL, 0, 0, 0);

#endif /* ICONV enabled/disabled */

    sqlite3_create_function_v2 (db, "DirNameFromPath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DirNameFromPath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FullFileNameFromPath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FullFileNameFromPath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FileNameFromPath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FileNameFromPath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FileExtFromPath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FileExtFromPath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToLinestring", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToLinestring, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToPolygon", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToPolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToMultiPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToMultiPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToMultiLinestring", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToMultiLinestring, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToMultiPolygon", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToMultiPolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToGeometryCollection", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToGeometryCollection, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToMulti", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToMulti, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Multi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToMulti, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToSingle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToSingle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXY", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYZ", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYZ, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYZ", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYZ, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYM", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYM", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYZM", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYZM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastToXYZM", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastToXYZM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ExtractMultiPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ExtractMultiPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ExtractMultiLinestring", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ExtractMultiLinestring, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ExtractMultiPolygon", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ExtractMultiPolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Reverse", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Reverse, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ForceLHR", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ForcePolygonCW, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ForcePolygonCW", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ForcePolygonCW, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ForcePolygonCCW", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ForcePolygonCCW, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsPolygonCW", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsPolygonCW, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsPolygonCCW", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsPolygonCCW, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Dimension", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Dimension, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Dimension", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Dimension, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CoordDimension", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CoordDimension, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NDims", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NDims, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryType", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryType, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryType", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryType, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryAliasType", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryAliasType, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SridFromAuthCRS", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SridFromAuthCRS, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SRID", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SRID", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SetSRID", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsEmpty", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsEmpty, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsEmpty", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsEmpty, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Is3D", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Is3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsMeasured", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsMeasured, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Envelope", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Envelope, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Envelope", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Envelope, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Expand", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Expand, 0, 0, 0);
    sqlite3_create_function_v2 (db, "X", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_X, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Y", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Y, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Z", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Z, 0, 0, 0);
    sqlite3_create_function_v2 (db, "M", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_M, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_X", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_X, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Y", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Y, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Z", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Z, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_M", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_M, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MinX", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMinX, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MinY", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMinY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MinZ", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MinZ, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MinM", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MinM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MaxX", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMaxX, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MaxY", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMaxY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MaxZ", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MaxZ, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MaxM", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MaxM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "NumPoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NumPoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StartPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_StartPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "EndPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_EndPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_StartPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_StartPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_EndPoint", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_EndPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ExteriorRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ExteriorRing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ExteriorRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ExteriorRing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "NumInteriorRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumInteriorRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "NumInteriorRings", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumInteriorRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NumInteriorRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumInteriorRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "InteriorRingN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_InteriorRingN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_InteriorRingN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_InteriorRingN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "NumGeometries", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NumGeometries", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NumGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeometryN", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeometryN, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MBRContains", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrContains, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrDisjoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrDisjoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MBREqual", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrEqual, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrIntersects", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_EnvIntersects", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_EnvIntersects", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_EnvIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_EnvelopesIntersects", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_EnvelopesIntersects", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_EnvIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MBROverlaps", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrOverlaps, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrTouches", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrTouches, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrWithin", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrWithin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ShiftCoords", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ShiftCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ShiftCoordinates", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ShiftCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Translate", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Translate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Shift_Longitude", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ShiftLongitude, 0, 0, 0);
    sqlite3_create_function_v2 (db, "NormalizeLonLat", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NormalizeLonLat, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ScaleCoords", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ScaleCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ScaleCoordinates", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ScaleCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ScaleCoords", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ScaleCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ScaleCoordinates", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ScaleCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RotateCoords", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RotateCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RotateCoordinates", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RotateCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ReflectCoords", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ReflectCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ReflectCoordinates", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ReflectCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SwapCoords", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SwapCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SwapCoordinates", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SwapCoords, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BuildMbr", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_BuildMbr1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BuildMbr", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_BuildMbr2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BuildCircleMbr", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_BuildCircleMbr1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BuildCircleMbr", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_BuildCircleMbr2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Extent", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Extent_step, fnct_Extent_final, 0);
    sqlite3_create_function_v2 (db, "MbrMinX", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMinX, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrMaxX", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMaxX, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrMinY", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMinY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MbrMaxY", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_MbrMaxY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TinyPointEncode", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_tiny_point_encode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeometryPointEncode", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_geometry_point_encode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Point", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePoint1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePoint1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePoint2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointZ", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointZ1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointZ", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointZ2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointM", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointM1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointM", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointM2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointZM", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointZM1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePointZM", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePointZM2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeLine", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_MakeLine_step, fnct_MakeLine_final, 0);
    sqlite3_create_function_v2 (db, "MakeLine", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeLine, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircle", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircle", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircle", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipse", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipse, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipse", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipse, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipse", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipse, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeArc", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeArc", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeArc", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticArc", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticArc", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticArc", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticArc, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularSector", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularSector", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularSector", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularStripe", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularStripe, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularStripe", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularStripe, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeCircularStripe", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeCircularStripe, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticSector", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticSector", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeEllipticSector", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeEllipticSector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Collect", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Collect_step, fnct_Collect_final, 0);
    sqlite3_create_function_v2 (db, "Collect", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Collect, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Collect", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Collect_step, fnct_Collect_final, 0);
    sqlite3_create_function_v2 (db, "ST_Collect", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Collect, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BuildMbrFilter", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_BuildMbrFilter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FilterMbrWithin", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FilterMbrWithin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FilterMbrContains", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FilterMbrContains, 0, 0, 0);
    sqlite3_create_function_v2 (db, "FilterMbrIntersects", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_FilterMbrIntersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LinesFromRings", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesFromRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LinesFromRings", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesFromRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LinesFromRings", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesFromRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LinesFromRings", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesFromRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NPoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_nrings", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NRings, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ToGARS", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToGARS, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GARSMbr", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GARSMbr, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SnapToGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SnapToGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SnapToGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SnapToGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SnapToGrid", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SnapToGrid", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SnapToGrid", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SnapToGrid", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SnapToGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AddPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AddPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AddPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RemovePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemovePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_RemovePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemovePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SetPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SetPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SetStartPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetStartPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SetStartPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetStartPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SetEndPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetEndPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SetEndPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SetEndPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePolygon", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MakePolygon", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakePolygon", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePolygon, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MakePolygon", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakePolygon, 0, 0, 0);

    sqlite3_create_function_v2 (db, "ATM_Create", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Create", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Create", 12,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_CreateTranslate", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateTranslate, 0,
				0, 0);
    sqlite3_create_function_v2 (db, "ATM_CreateTranslate", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateTranslate, 0,
				0, 0);
    sqlite3_create_function_v2 (db, "ATM_CreateScale", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateScale, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_CreateScale", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateScale, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_CreateRotate", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateRotate, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_CreateXRoll", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateXRoll, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_CreateYRoll", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateYRoll, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_CreateZRoll", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_CreateRotate, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_Multiply", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Multiply, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Translate", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Translate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Translate", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Translate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Scale", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Scale, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Scale", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Scale, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Rotate", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Rotate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_XRoll", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_XRoll, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_YRoll", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_YRoll, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_ZRoll", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Rotate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Determinant", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Determinant, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_IsInvertible", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_IsInvertible, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "ATM_Invert", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_Invert, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_IsValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_AsText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AffineTransformMatrix_AsText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Transform", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AffineTransformMatrix_GeometryTransform,
				0, 0, 0);
    sqlite3_create_function_v2 (db, "ATM_Transform", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AffineTransformMatrix_GeometryTransform,
				0, 0, 0);

#ifdef ENABLE_GCP		/* only if ControlPoints enabled */
    sqlite3_create_function_v2 (db, "GCP_Compute", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_GroundControlPoints_Compute_step,
				fnct_GroundControlPoints_Compute_final, 0);
    sqlite3_create_function_v2 (db, "GCP_Compute", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_GroundControlPoints_Compute_step,
				fnct_GroundControlPoints_Compute_final, 0);
    sqlite3_create_function_v2 (db, "GCP_Transform", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GroundControlPoints_GeometryTransform, 0,
				0, 0);
    sqlite3_create_function_v2 (db, "GCP_Transform", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GroundControlPoints_GeometryTransform, 0,
				0, 0);
    sqlite3_create_function_v2 (db, "GCP_IsValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GroundControlPoints_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GCP_AsText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GroundControlPoints_AsText, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GCP2ATM", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GroundControlPoints_ToATM, 0, 0, 0);
#endif /* end including GCP */

#ifndef OMIT_GEOS		/* including GEOS */
    sqlite3_create_function_v2 (db, "BuildArea", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BuildArea, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BuildArea", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BuildArea, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Polygonize", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Polygonize_step, fnct_Polygonize_final, 0);
    sqlite3_create_function_v2 (db, "ST_Polygonize", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Polygonize_step, fnct_Polygonize_final, 0);
#endif /* end including GEOS */

    sqlite3_create_function_v2 (db, "DissolveSegments", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DissolveSegments, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DissolveSegments", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DissolveSegments, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DissolvePoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DissolvePoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DissolvePoints", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DissolvePoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CollectionExtract", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CollectionExtract, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_CollectionExtract", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CollectionExtract, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_InterpolatePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_InterpolatePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AddMeasure", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddMeasure, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Locate_Along_Measure", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LocateBetweenMeasures, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LocateAlong", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LocateBetweenMeasures, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Locate_Between_Measures", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LocateBetweenMeasures, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LocateBetween", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LocateBetweenMeasures, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValidTrajectory", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidTrajectory, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_TrajectoryInterpolatePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TrajectoryInterpolatePoint, 0, 0, 0);

#ifndef OMIT_GEOCALLBACKS	/* supporting RTree geometry callbacks */
    sqlite3_rtree_geometry_callback (db, "RTreeWithin", fnct_RTreeIntersects,
				     0);
    sqlite3_rtree_geometry_callback (db, "RTreeContains",
				     fnct_RTreeIntersects, 0);
    sqlite3_rtree_geometry_callback (db, "RTreeIntersects",
				     fnct_RTreeIntersects, 0);
    sqlite3_rtree_geometry_callback (db, "RTreeDistWithin",
				     fnct_RTreeDistWithin, 0);
#endif /* end RTree geometry callbacks */

/* some BLOB/JPEG/EXIF functions */
    sqlite3_create_function_v2 (db, "IsGeometryBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsGeometryBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsTinyPointBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsTinyPointBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsZipBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsZipBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsPdfBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsPdfBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsTiffBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsTiffBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsGifBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsGifBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsPngBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsPngBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsJpegBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsJpegBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsExifBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsExifBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsExifGpsBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsExifGpsBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsWebpBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsWebPBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsJP2Blob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsJP2Blob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromExifGpsBlob", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GeomFromExifGpsBlob, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetMimeType", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GetMimeType, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CountUnsafeTriggers", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CountUnsafeTriggers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CheckDuplicateRows", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CheckDuplicateRows, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RemoveDuplicateRows", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RemoveDuplicateRows, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RemoveDuplicateRows", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RemoveDuplicateRows, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 9,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 10,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 11,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 12,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 13,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 14,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 15,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ElementaryGeometries", 16,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ElementaryGeometries, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DropGeoTable", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DropGeoTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DropGeoTable", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DropGeoTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DropGeoTable", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_DropGeoTable, 0, 0, 0);

    sqlite3_create_function_v2 (db, "SqlProc_GetLastError", 0, SQLITE_UTF8,
				cache, fnct_sp_get_last_error, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_GetLogfile", 0, SQLITE_UTF8,
				cache, fnct_sp_get_logfile, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_FromText", 1, SQLITE_UTF8,
				cache, fnct_sp_from_text, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_FromText", 2, SQLITE_UTF8,
				cache, fnct_sp_from_text, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_IsValid", 1, SQLITE_UTF8, 0,
				fnct_sp_is_valid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_NumVariables", 1, SQLITE_UTF8,
				0, fnct_sp_var_count, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_VariableN", 2, SQLITE_UTF8, 0,
				fnct_sp_variable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_AllVariables", 1, SQLITE_UTF8,
				0, fnct_sp_all_variables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_VarValue", 2, SQLITE_UTF8, 0,
				fnct_sp_var_arg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_IsValidVarValue", 1, SQLITE_UTF8,
				0, fnct_sp_is_valid_var, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_RawSQL", 1, SQLITE_UTF8, 0,
				fnct_sp_raw_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 1, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 2, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 3, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 4, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 5, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 6, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 7, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 8, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 9, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 10, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 11, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 12, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 13, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 14, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 15, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 16, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_CookedSQL", 17, SQLITE_UTF8,
				cache, fnct_sp_cooked_sql, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 1, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 2, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 3, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 4, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 5, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 6, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 7, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 8, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 9, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 10, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 11, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 12, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 13, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 14, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 15, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 16, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Execute", 17, SQLITE_UTF8, cache,
				fnct_sp_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SqlProc_Exit", 0, SQLITE_UTF8, cache,
				fnct_sp_exit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Exit", 0, SQLITE_UTF8, cache,
				fnct_sp_exit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_CreateTables", 0, SQLITE_UTF8,
				cache, fnct_sp_create_tables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Register", 3, SQLITE_UTF8,
				cache, fnct_sp_register, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Get", 1, SQLITE_UTF8, cache,
				fnct_sp_get, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Delete", 1, SQLITE_UTF8,
				cache, fnct_sp_delete, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_UpdateTitle", 2, SQLITE_UTF8,
				cache, fnct_sp_update_title, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_UpdateSqlBody", 2,
				SQLITE_UTF8, cache, fnct_sp_update_sql, 0, 0,
				0);
    sqlite3_create_function_v2 (db, "StoredVar_Register", 3, SQLITE_UTF8,
				cache, fnct_sp_var_register, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredVar_Get", 1, SQLITE_UTF8, cache,
				fnct_sp_var_get, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredVar_GetValue", 1, SQLITE_UTF8,
				cache, fnct_sp_var_get_value, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredVar_Delete", 1, SQLITE_UTF8, cache,
				fnct_sp_var_delete, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredVar_UpdateTitle", 2, SQLITE_UTF8,
				cache, fnct_sp_var_update_title, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredVar_UpdateValue", 2, SQLITE_UTF8,
				cache, fnct_sp_var_update_value, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 1, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 2, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 3, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 4, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 5, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 6, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 7, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 8, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 9, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 10, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 11, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 12, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 13, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 14, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 15, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 16, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);
    sqlite3_create_function_v2 (db, "StoredProc_Execute", 17, SQLITE_UTF8,
				cache, fnct_sp_stored_execute, 0, 0, 0);

    sqlite3_create_function_v2 (db, "CreateRoutingNodes", 5, SQLITE_UTF8,
				cache, fnct_create_routing_nodes, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRouting", 7, SQLITE_UTF8,
				cache, fnct_create_routing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRouting", 10, SQLITE_UTF8,
				cache, fnct_create_routing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRouting", 12, SQLITE_UTF8,
				cache, fnct_create_routing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRouting", 13, SQLITE_UTF8,
				cache, fnct_create_routing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateRouting_GetLastError", 0,
				SQLITE_UTF8, cache,
				fnct_create_routing_get_last_error, 0, 0, 0);

/*
// enabling BlobFromFile, BlobToFile and XB_LoadXML, XB_StoreXML, 
// ExportDXF and other import/export functions
//
// these functions could potentially introduce serious security issues,
// most notably when invoked from within some Trigger
// - BlobToFile: some arbitrary code, possibly harmfull (e.g. virus or 
//   trojan) could be installed on the local file-system, the user being
//   completely unaware of this
// - BlobFromFile: some file could be maliciously "stolen" from the local
//   file system and then inseted into the DB
// - the same is for XB_LoadXML and XB_StoreXML
// - ExportDXF could potentially flood the local file-system by
//   outputting a huge size of data
//
// so by default such functions are disabled.
// if for any good/legitimate reason the user really wants to enable them
// the following environment variable has to be explicitly declared:
//
// SPATIALITE_SECURITY=relaxed
//
*/
    security_level = getenv ("SPATIALITE_SECURITY");
    if (security_level == NULL)
	;
    else if (strcasecmp (security_level, "relaxed") == 0)
      {
	  sqlite3_create_function_v2 (db, "BlobFromFile", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_BlobFromFile, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "BlobToFile", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_BlobToFile, 0, 0, 0);

#ifndef OMIT_GEOS		/* only if GEOS is enabled */

	  sqlite3_create_function_v2 (db, "ImportDXF", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ImportDXF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDXF", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ImportDXF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDXFfromDir", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ImportDXFfromDir, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDXFfromDir", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ImportDXFfromDir, 0, 0, 0);

#endif /* GEOS enabled */

	  sqlite3_create_function_v2 (db, "ExportDXF", 9,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ExportDXF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportDXF", 10,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ExportDXF, 0, 0, 0);

#ifndef OMIT_ICONV		/* ICONV is supported */

	  sqlite3_create_function_v2 (db, "ImportDBF", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDBF", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDBF", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportDBF", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportDBF", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportDBF", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportDBF, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 9,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 10,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 11,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 12,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 13,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportSHP", 14,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportSHP", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportSHP", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportSHP, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportSHP", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportSHP, 0, 0, 0);

#endif /* ICONV enabled */

	  sqlite3_create_function_v2 (db, "ExportKML", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportKML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportKML", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportKML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportKML", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportKML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportKML", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportKML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportGeoJSON", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportGeoJSON, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportGeoJSON", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportGeoJSON, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ExportGeoJSON", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ExportGeoJSON, 0, 0, 0);

	  sqlite3_create_function_v2 (db, "eval", 1, SQLITE_UTF8, 0,
				      fnct_EvalFunc, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "eval", 2, SQLITE_UTF8, 0,
				      fnct_EvalFunc, 0, 0, 0);

	  sqlite3_create_function_v2 (db, "SqlProc_FromFile", 1, SQLITE_UTF8,
				      cache, fnct_sp_from_file, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "SqlProc_FromFile", 2, SQLITE_UTF8,
				      cache, fnct_sp_from_file, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "SqlProc_SetLogfile", 1,
				      SQLITE_UTF8, cache, fnct_sp_set_logfile,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "SqlProc_SetLogfile", 2,
				      SQLITE_UTF8, cache, fnct_sp_set_logfile,
				      0, 0, 0);

#ifdef ENABLE_LIBXML2		/* including LIBXML2 */

	  sqlite3_create_function_v2 (db, "XB_LoadXML", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_XB_LoadXML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "XB_StoreXML", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_XB_StoreXML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "XB_StoreXML", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_XB_StoreXML, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportWFS", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportWFS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportWFS", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportWFS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportWFS", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportWFS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportWFS", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportWFS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportWFS", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportWFS, 0, 0, 0);

#endif /* end including LIBXML2 */

#ifndef OMIT_FREEXL		/* FREEXL is enabled */
	  sqlite3_create_function_v2 (db, "ImportXLS", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportXLS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportXLS", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportXLS, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ImportXLS", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				      fnct_ImportXLS, 0, 0, 0);
#endif /* end FREEXL support */

      }


/* global settings */

#ifdef ENABLE_GEOPACKAGE	/* GEOPACKAGE enabled: supporting GPKG geometries */
    sqlite3_create_function_v2 (db, "EnableGpkgMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_enableGpkgMode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DisableGpkgMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_disableGpkgMode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetGpkgMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_getGpkgMode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "EnableGpkgAmphibiousMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_enableGpkgAmphibiousMode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DisableGpkgAmphibiousMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_disableGpkgAmphibiousMode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetGpkgAmphibiousMode", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_getGpkgAmphibiousMode, 0, 0, 0);
#endif /* end GPKG conditional */

    sqlite3_create_function_v2 (db, "SetDecimalPrecision", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_setDecimalPrecision, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetDecimalPrecision", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_getDecimalPrecision, 0, 0, 0);

    sqlite3_create_function_v2 (db, "*Add-Shapefile+Extent", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_addShapefileExtent, 0, 0, 0);
    sqlite3_create_function_v2 (db, "*Remove-Shapefile+Extent", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_removeShapefileExtent, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetShapefileExtent", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_getShapefileExtent, 0, 0, 0);

    sqlite3_create_function_v2 (db, "IsLowASCII", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_isLowASCII, 0, 0, 0);

    sqlite3_create_function_v2 (db, "IsTinyPointEnabled", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_isTinyPointEnabled, 0, 0, 0);
    sqlite3_create_function_v2 (db, "EnableTinyPoint", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_enableTinyPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DisableTinyPoint", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_disableTinyPoint, 0, 0, 0);

    sqlite3_create_function_v2 (db, "MakeStringList", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_make_string_list_step,
				fnct_make_string_list_final, 0);
    sqlite3_create_function_v2 (db, "MakeStringList", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_make_string_list_step,
				fnct_make_string_list_final, 0);

/* some Geodesic functions */
    sqlite3_create_function_v2 (db, "GreatCircleLength", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GreatCircleLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicLength", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicArcLength", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicArcLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicArcLength", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicArcLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicChordLength", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicChordLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicChordLength", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicChordLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicCentralAngle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicCentralAngle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicCentralAngle", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicCentralAngle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicArcArea", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicArcArea, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeodesicArcHeight", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeodesicArcHeight, 0, 0, 0);

/* some Length Unit conversion functions */
    sqlite3_create_function_v2 (db, "CvtToKm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToKm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToDm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToDm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToCm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToCm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToMm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToMm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToKmi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToKmi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToIn", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToIn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToMi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToMi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToFath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToFath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToCh, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToLink", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToLink, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToUsIn", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToUsIn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToUsFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToUsFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToUsYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToUsYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToUsCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToUsCh, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToUsMi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToUsMi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToIndFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToIndFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToIndYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToIndYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtToIndCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtToIndCh, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromKm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromKm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromDm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromDm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromCm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromCm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromMm", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromMm, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromKmi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromKmi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromIn", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromIn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromMi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromMi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromFath", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromFath, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromCh, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromLink", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromLink, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromUsIn", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromUsIn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromUsFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromUsFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromUsYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromUsYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromUsCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromUsCh, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromUsMi", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromUsMi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromIndFt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromIndFt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromIndYd", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromIndYd, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CvtFromIndCh", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_cvtFromIndCh, 0, 0, 0);

/* DMS (Degrees/Minutes/Seconds) to DD (decimal degrees) */
    sqlite3_create_function_v2 (db, "LongitudeFromDMS", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_longFromDMS, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LatitudeFromDMS", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_latFromDMS, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LongLatToDMS", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_toDMS, 0, 0, 0);

    if (cache != NULL)
      {
	  /* Sequences */
	  sqlite3_create_function_v2 (db, "sequence_currval", 1,
				      SQLITE_UTF8, cache,
				      fnct_sequence_currval, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "sequence_lastval", 0,
				      SQLITE_UTF8, cache,
				      fnct_sequence_lastval, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "sequence_nextval", 1,
				      SQLITE_UTF8, cache,
				      fnct_sequence_nextval, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "sequence_setval", 2,
				      SQLITE_UTF8, cache,
				      fnct_sequence_setval, 0, 0, 0);
      }

#ifndef OMIT_MATHSQL		/* supporting SQL math functions */

/* some extra math functions */
    sqlite3_create_function_v2 (db, "acos", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_acos, 0, 0, 0);
    sqlite3_create_function_v2 (db, "asin", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_asin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "atan", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_atan, 0, 0, 0);
    sqlite3_create_function_v2 (db, "atan2", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_atan2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ceil", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_ceil, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ceiling", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_ceil, 0, 0, 0);
    sqlite3_create_function_v2 (db, "cos", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_cos, 0, 0, 0);
    sqlite3_create_function_v2 (db, "cot", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_cot, 0, 0, 0);
    sqlite3_create_function_v2 (db, "degrees", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_degrees, 0, 0, 0);
    sqlite3_create_function_v2 (db, "exp", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_exp, 0, 0, 0);
    sqlite3_create_function_v2 (db, "floor", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_floor, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ln", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_logn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "log", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_logn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "log", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_logn2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "log2", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_log_2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "log10", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_log_10, 0, 0, 0);
    sqlite3_create_function_v2 (db, "pi", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_pi, 0, 0, 0);
    sqlite3_create_function_v2 (db, "pow", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_pow, 0, 0, 0);
    sqlite3_create_function_v2 (db, "power", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_pow, 0, 0, 0);
    sqlite3_create_function_v2 (db, "radians", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_radians, 0, 0, 0);
    sqlite3_create_function_v2 (db, "sign", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_sign, 0, 0, 0);
    sqlite3_create_function_v2 (db, "sin", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_sin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "stddev_pop", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_math_stddev_step,
				fnct_math_stddev_pop_final, 0);
    sqlite3_create_function_v2 (db, "stddev_samp", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_math_stddev_step,
				fnct_math_stddev_samp_final, 0);
    sqlite3_create_function_v2 (db, "sqrt", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_sqrt, 0, 0, 0);
    sqlite3_create_function_v2 (db, "tan", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_math_tan, 0, 0, 0);
    sqlite3_create_function_v2 (db, "var_pop", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_math_stddev_step,
				fnct_math_var_pop_final, 0);
    sqlite3_create_function_v2 (db, "var_samp", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0, 0,
				fnct_math_stddev_step,
				fnct_math_var_samp_final, 0);

#endif /* end supporting SQL math functions */

#ifndef OMIT_PROJ		/* including PROJ.4 */

    sqlite3_create_function_v2 (db, "Transform", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Transform, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Transform", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Transform, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TransformXY", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TransformXY, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_TransformXY", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TransformXY, 0, 0, 0);

#endif /* end including PROJ.4 */

#ifndef OMIT_GEOS		/* including GEOS */

    sqlite3_create_function_v2 (db, "GEOS_GetLastErrorMsg", 0, SQLITE_UTF8,
				cache, fnct_GEOS_GetLastErrorMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GEOS_GetLastWarningMsg", 0, SQLITE_UTF8,
				cache, fnct_GEOS_GetLastWarningMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GEOS_GetLastAuxErrorMsg", 0, SQLITE_UTF8,
				cache, fnct_GEOS_GetLastAuxErrorMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GEOS_GetCriticalPointFromMsg", 0,
				SQLITE_UTF8, cache,
				fnct_GEOS_GetCriticalPointFromMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GEOS_GetCriticalPointFromMsg", 1,
				SQLITE_UTF8, cache,
				fnct_GEOS_GetCriticalPointFromMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidReason", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidReason, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidReason", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidReason, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValidReason", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidReason, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValidReason", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidReason, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidDetail", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidDetail, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidDetail", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidDetail, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValidDetail", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidDetail, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValidDetail", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValidDetail, 0, 0, 0);

    sqlite3_create_function_v2 (db, "Boundary", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Boundary, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Boundary", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Boundary, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsClosed", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsClosed, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsClosed", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsClosed, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsSimple", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsSimple, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsSimple", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsSimple, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsRing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsRing", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsRing, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_IsValid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GLength", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Length, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GLength", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Length, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Length", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Length, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Length", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Length, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Perimeter", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Perimeter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Perimeter", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Perimeter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Perimeter", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Perimeter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Perimeter", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Perimeter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Area", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Area, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Area", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Area, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Circularity", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Circularity, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Centroid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Centroid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Centroid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Centroid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PointOnSurface", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointOnSurface, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_PointOnSurface", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PointOnSurface, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Simplify", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Simplify, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Simplify", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Simplify, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Generalize", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Simplify, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SimplifyPreserveTopology", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SimplifyPreserveTopology, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SimplifyPreserveTopology", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SimplifyPreserveTopology, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ConvexHull", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConvexHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ConvexHull", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConvexHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Buffer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Buffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Buffer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Buffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Buffer", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Buffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Buffer", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Buffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Intersection", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Intersection, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Intersection", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Intersection, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GUnion", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Union_step, fnct_Union_final, 0);
    sqlite3_create_function_v2 (db, "GUnion", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Union, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Union", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache, 0,
				fnct_Union_step, fnct_Union_final, 0);
    sqlite3_create_function_v2 (db, "ST_Union", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Union, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Difference", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Difference, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Difference", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Difference, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SymDifference", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SymDifference, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SymDifference", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SymDifference, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Equals", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Equals, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Equals", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Equals, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Intersects", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Intersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Intersects", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Intersects, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Disjoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Disjoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Disjoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Disjoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Overlaps", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Overlaps, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Overlaps", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Overlaps, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Crosses", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Crosses, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Crosses", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Crosses, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Touches", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Touches, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Touches", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Touches, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Within", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Within, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Within", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Within, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Contains", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Contains, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Contains", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Contains, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Relate", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Relate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Relate", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Relate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Relate", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Relate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Relate", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Relate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_RelateMatch", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RelateMatch, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Distance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Distance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Distance", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Distance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Distance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Distance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Distance", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Distance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PtDistWithin", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PtDistWithin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "PtDistWithin", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_PtDistWithin, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdMPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdMPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromWKB1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromWKB2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdMPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromWKB1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "BdMPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromWKB2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdMPolyFromText", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromText1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdMPolyFromText", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromText2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromWKB1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdPolyFromWKB2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdMPolyFromWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromWKB1, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_BdMPolyFromWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_BdMPolyFromWKB2, 0, 0, 0);
    sqlite3_create_function_v2 (db, "OffsetCurve", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_OffsetCurve, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_OffsetCurve", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_OffsetCurve, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SingleSidedBuffer", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SingleSidedBuffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SingleSidedBuffer", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SingleSidedBuffer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HausdorffDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HausdorffDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_HausdorffDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HausdorffDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SharedPaths", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SharedPaths, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SharedPaths", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SharedPaths, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Covers", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Covers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Covers", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Covers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CoveredBy", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CoveredBy, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_CoveredBy", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CoveredBy, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Line_Interpolate_Point", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineInterpolatePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Line_Interpolate_Point", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineInterpolatePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Line_Interpolate_Equidistant_Points", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineInterpolateEquidistantPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Line_Interpolate_Equidistant_Points",
				2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineInterpolateEquidistantPoints, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Line_Locate_Point", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineLocatePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Line_Locate_Point", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineLocatePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Line_Substring", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineSubstring, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Line_Substring", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineSubstring, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ClosestPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ClosestPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ClosestPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ClosestPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ShortestLine", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ShortestLine, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ShortestLine", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ShortestLine, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Snap", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Snap, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Snap", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Snap, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LineMerge", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineMerge, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineMerge", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineMerge, 0, 0, 0);
    sqlite3_create_function_v2 (db, "UnaryUnion", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_UnaryUnion, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_UnaryUnion", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_UnaryUnion, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SquareGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SquareGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SquareGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SquareGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SquareGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SquareGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SquareGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TriangularGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TriangularGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TriangularGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_TriangularGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_TriangularGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_TriangularGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TriangularGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HexagonalGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HexagonalGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "HexagonalGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_HexagonalGrid", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_HexagonalGrid", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_HexagonalGrid", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_HexagonalGrid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "LinesCutAtNodes", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesCutAtNodes, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LinesCutAtNodes", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LinesCutAtNodes, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RingsCutAtNodes", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RingsCutAtNodes, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_RingsCutAtNodes", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RingsCutAtNodes, 0, 0, 0);

#ifdef GEOS_ADVANCED		/* GEOS advanced features - 3.4.0 */

    sqlite3_create_function_v2 (db, "DelaunayTriangulation", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DelaunayTriangulation", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DelaunayTriangulation", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DelaunayTriangulation", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DelaunayTriangulation", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DelaunayTriangulation", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DelaunayTriangulation, 0, 0, 0);
    sqlite3_create_function_v2 (db, "VoronojDiagram", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "VoronojDiagram", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "VoronojDiagram", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "VoronojDiagram", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_VoronojDiagram", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_VoronojDiagram", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_VoronojDiagram", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_VoronojDiagram", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_VoronojDiagram, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ConcaveHull", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ConcaveHull", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ConcaveHull", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ConcaveHull", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ConcaveHull", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ConcaveHull", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ConcaveHull", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ConcaveHull", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ConcaveHull, 0, 0, 0);

#endif /* end GEOS advanced features */

#ifdef ENABLE_RTTOPO		/* enabling RTTOPO support */

    sqlite3_create_function_v2 (db, "RTTOPO_GetLastErrorMsg", 0, SQLITE_UTF8,
				0, fnct_RTTOPO_GetLastErrorMsg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RTTOPO_GetLastWarningMsg", 0,
				SQLITE_UTF8, 0, fnct_RTTOPO_GetLastWarningMsg,
				0, 0, 0);

    sqlite3_create_function_v2 (db, "MakeValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MakeValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MakeValidDiscarded", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeValidDiscarded, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MakeValidDiscarded", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MakeValidDiscarded, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Area", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Area, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Area", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Area, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Circularity", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Circularity, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Segmentize", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Segmentize, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Segmentize", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Segmentize, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Azimuth", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Azimuth, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Azimuth", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Azimuth, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Project", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Project, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Project", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Project, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeoHash", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeoHash, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeoHash", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeoHash, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeoHash", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeoHash, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_GeoHash", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GeoHash, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsX3D", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsX3D", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsX3D", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsX3D", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsX3D", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsX3D", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsX3D", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsX3D", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsX3D, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_3DDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_3DDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "MaxDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MaxDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MaxDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MaxDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_3DMaxDistance", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_3DMaxDistance, 0, 0, 0);
    sqlite3_create_function_v2 (db, "Split", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Split, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Split", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Split, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SplitLeft", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SplitLeft, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SplitLeft", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SplitLeft, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SplitRight", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SplitRight, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SplitRight", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SplitRight, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Node", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Node, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SelfIntersections", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SelfIntersections, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SelfIntersections", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SelfIntersections, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_3dLength", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_3dLength, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromTWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromTWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_FromTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsTWKB", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ToTWKB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsEncodedPolyline", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsEncodedPolyline, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AsEncodedPolyline", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AsEncodedPolyline, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromEncodedPolyline", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromEncodedPolyline, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LineFromEncodedPolyline", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LineFromEncodedPolyline, 0, 0, 0);

#endif /* end RTTOPO support */

    sqlite3_create_function_v2 (db, "ST_Cutter", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Cutter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Cutter", 8,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Cutter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_Cutter", 9,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_Cutter, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetCutterMessage", 0,
				SQLITE_UTF8, cache,
				fnct_GetCutterMessage, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DrapeLine", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DrapeLine, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DrapeLine", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DrapeLine, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DrapeLineExceptions", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DrapeLineExceptions, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DrapeLineExceptions", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DrapeLineExceptions, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_DrapeLineExceptions", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DrapeLineExceptions, 0, 0, 0);

#endif /* end including GEOS */

#ifdef ENABLE_LIBXML2		/* including LIBXML2 */

    sqlite3_create_function_v2 (db, "CreateStylingTables", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateStylingTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateStylingTables", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateStylingTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateStylingTables", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateStylingTables, 0, 0, 0);
    sqlite3_create_function (db, "SE_RegisterVectorCoverage", 3, SQLITE_ANY,
			     0, fnct_RegisterVectorCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterVectorCoverage", 5, SQLITE_ANY,
			     0, fnct_RegisterVectorCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterVectorCoverage", 7, SQLITE_ANY,
			     0, fnct_RegisterVectorCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterSpatialViewCoverage", 3,
			     SQLITE_ANY, 0, fnct_RegisterSpatialViewCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterSpatialViewCoverage", 5,
			     SQLITE_ANY, 0, fnct_RegisterSpatialViewCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterSpatialViewCoverage", 7,
			     SQLITE_ANY, 0, fnct_RegisterSpatialViewCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterVirtualShapeCoverage", 3,
			     SQLITE_ANY, 0, fnct_RegisterVirtualShapeCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterVirtualShapeCoverage", 5,
			     SQLITE_ANY, 0, fnct_RegisterVirtualShapeCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterVirtualShapeCoverage", 6,
			     SQLITE_ANY, 0, fnct_RegisterVirtualShapeCoverage,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoGeoCoverage", 2, SQLITE_ANY,
			     0, fnct_RegisterTopoGeoCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoGeoCoverage", 4, SQLITE_ANY,
			     0, fnct_RegisterTopoGeoCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoGeoCoverage", 6, SQLITE_ANY,
			     0, fnct_RegisterTopoGeoCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoNetCoverage", 2, SQLITE_ANY,
			     0, fnct_RegisterTopoNetCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoNetCoverage", 4, SQLITE_ANY,
			     0, fnct_RegisterTopoNetCoverage, 0, 0);
    sqlite3_create_function (db, "SE_RegisterTopoNetCoverage", 6, SQLITE_ANY,
			     0, fnct_RegisterTopoNetCoverage, 0, 0);
    sqlite3_create_function (db, "SE_UnRegisterVectorCoverage", 1, SQLITE_ANY,
			     0, fnct_UnregisterVectorCoverage, 0, 0);
    sqlite3_create_function (db, "SE_SetVectorCoverageInfos", 3, SQLITE_ANY,
			     0, fnct_SetVectorCoverageInfos, 0, 0);
    sqlite3_create_function (db, "SE_SetVectorCoverageInfos", 5, SQLITE_ANY,
			     0, fnct_SetVectorCoverageInfos, 0, 0);
    sqlite3_create_function (db, "SE_SetVectorCoverageCopyright", 2,
			     SQLITE_ANY, 0, fnct_SetVectorCoverageCopyright,
			     0, 0);
    sqlite3_create_function (db, "SE_SetVectorCoverageCopyright", 3,
			     SQLITE_ANY, 0, fnct_SetVectorCoverageCopyright,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterVectorCoverageSrid", 2,
			     SQLITE_ANY, 0, fnct_RegisterVectorCoverageSrid,
			     0, 0);
    sqlite3_create_function (db, "SE_UnRegisterVectorCoverageSrid", 2,
			     SQLITE_ANY, 0, fnct_UnregisterVectorCoverageSrid,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterVectorCoverageKeyword", 2,
			     SQLITE_ANY, 0,
			     fnct_RegisterVectorCoverageKeyword, 0, 0);
    sqlite3_create_function (db, "SE_UnRegisterVectorCoverageKeyword", 2,
			     SQLITE_ANY, 0,
			     fnct_UnregisterVectorCoverageKeyword, 0, 0);
    sqlite3_create_function (db, "SE_UpdateVectorCoverageExtent", 0,
			     SQLITE_ANY, 0, fnct_UpdateVectorCoverageExtent,
			     0, 0);
    sqlite3_create_function (db, "SE_UpdateVectorCoverageExtent", 1,
			     SQLITE_ANY, 0, fnct_UpdateVectorCoverageExtent,
			     0, 0);
    sqlite3_create_function (db, "SE_UpdateVectorCoverageExtent", 2,
			     SQLITE_ANY, 0, fnct_UpdateVectorCoverageExtent,
			     0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterExternalGraphic", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterExternalGraphic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterExternalGraphic", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterExternalGraphic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnregisterExternalGraphic", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnregisterExternalGraphic, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterVectorStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterVectorStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterVectorStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterVectorStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterVectorStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterVectorStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_ReloadVectorStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ReloadVectorStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterVectorStyledLayer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterVectorStyledLayer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterVectorStyledLayer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterVectorStyledLayer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterRasterStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterRasterStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterRasterStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterRasterStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterRasterStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterRasterStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_ReloadRasterStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ReloadRasterStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterRasterStyledLayer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterRasterStyledLayer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterRasterStyledLayer", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterRasterStyledLayer, 0, 0, 0);
    sqlite3_create_function (db, "SE_RegisterRasterCoverageSrid", 2,
			     SQLITE_ANY, 0, fnct_RegisterRasterCoverageSrid,
			     0, 0);
    sqlite3_create_function (db, "SE_UnRegisterRasterCoverageSrid", 2,
			     SQLITE_ANY, 0, fnct_UnregisterRasterCoverageSrid,
			     0, 0);
    sqlite3_create_function (db, "SE_RegisterRasterCoverageKeyword", 2,
			     SQLITE_ANY, 0,
			     fnct_RegisterRasterCoverageKeyword, 0, 0);
    sqlite3_create_function (db, "SE_UnRegisterRasterCoverageKeyword", 2,
			     SQLITE_ANY, 0,
			     fnct_UnregisterRasterCoverageKeyword, 0, 0);
    sqlite3_create_function (db, "SE_UpdateRasterCoverageExtent", 0,
			     SQLITE_ANY, 0, fnct_UpdateRasterCoverageExtent,
			     0, 0);
    sqlite3_create_function (db, "SE_UpdateRasterCoverageExtent", 1,
			     SQLITE_ANY, 0, fnct_UpdateRasterCoverageExtent,
			     0, 0);
    sqlite3_create_function (db, "SE_UpdateRasterCoverageExtent", 2,
			     SQLITE_ANY, 0, fnct_UpdateRasterCoverageExtent,
			     0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterStyledGroupRaster", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterStyledGroupRaster, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterStyledGroupVector", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterStyledGroupVector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_SetStyledGroupInfos", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SetStyledGroupInfos, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterStyledGroup", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterStyledGroup, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterStyledGroupLayer", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterStyledGroupLayer, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterStyledGroupVector", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterStyledGroupVector, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterStyledGroupRaster", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterStyledGroupRaster, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_SetStyledGroupLayerPaintOrder", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SetStyledGroupLayerPaintOrder, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_SetStyledGroupVectorPaintOrder", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SetStyledGroupVectorPaintOrder, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_SetStyledGroupRasterPaintOrder", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_SetStyledGroupRasterPaintOrder, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterGroupStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterGroupStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterGroupStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_ReloadGroupStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ReloadGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_RegisterStyledGroupStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterStyledGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "SE_UnRegisterStyledGroupStyle", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_UnRegisterStyledGroupStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateIsoMetadataTables", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateIsoMetadataTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateIsoMetadataTables", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_CreateIsoMetadataTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetIsoMetadataId", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GetIsoMetadataId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RegisterIsoMetadata", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterIsoMetadata, 0, 0, 0);
    sqlite3_create_function_v2 (db, "RegisterIsoMetadata", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_RegisterIsoMetadata, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_Create", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_Create", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_Create", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_Create, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetPayload", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetPayload, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetPayload", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetPayload, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetDocument", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetDocument, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetDocument", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetDocument, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_SchemaValidate", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_SchemaValidate, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_Compress", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_Compress, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_Uncompress", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_Uncompress, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsValid", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsValid, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsSchemaValidated", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsSchemaValidated, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsCompressed", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsCompressed, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsIsoMetadata", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsIsoMetadata, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsSldSeVectorStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsSldSeVectorStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsSldSeRasterStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsSldSeRasterStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsSldStyle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsSldStyle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsSvg", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsSvg, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsGpx", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_IsGpx, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetSchemaURI", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetSchemaURI, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetInternalSchemaURI", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_GetInternalSchemaURI, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetFileId", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetFileId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetParentId", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetParentId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_SetFileId", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_SetFileId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_SetParentId", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_SetParentId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_AddFileId", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_AddFileId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_AddParentId", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_AddParentId, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetName", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetName, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetTitle", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetTitle, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetAbstract", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetAbstract, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetGeometry", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetGeometry, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_MLineFromGPX", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_MLineFromGPX, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetDocumentSize", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetDocumentSize, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetEncoding", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_XB_GetEncoding, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetLastParseError", 0, SQLITE_UTF8,
				cache, fnct_XB_GetLastParseError, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetLastValidateError", 0, SQLITE_UTF8,
				cache, fnct_XB_GetLastValidateError, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_IsValidXPathExpression", 1,
				SQLITE_UTF8, cache,
				fnct_XB_IsValidXPathExpression, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_GetLastXPathError", 0, SQLITE_UTF8,
				cache, fnct_XB_GetLastXPathError, 0, 0, 0);
    sqlite3_create_function_v2 (db, "XB_CacheFlush", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_XB_CacheFlush, 0, 0, 0);

#endif /* end including LIBXML2 */

#ifdef ENABLE_GEOPACKAGE	/* enabling GeoPackage extensions */

    sqlite3_create_function_v2 (db, "AutoGPKGStart", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoGPKGStart, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoGPKGStart", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoGPKGStart, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoGPKGStop", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoGPKGStop, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AutoGPKGStop", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_AutoGPKGStop, 0, 0, 0);

    /* not yet finalised geopackage raster functions, plus some convenience API */
    sqlite3_create_function_v2 (db, "gpkgCreateBaseTables", 0,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgCreateBaseTables, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgInsertEpsgSRID", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgInsertEpsgSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgCreateTilesTable", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgCreateTilesTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgCreateTilesZoomLevel", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgCreateTilesZoomLevel, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgAddTileTriggers", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgAddTileTriggers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgGetNormalZoom", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgGetNormalZoom, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgGetNormalRow", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgGetNormalRow, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgGetImageType", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgGetImageType, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgAddGeometryColumn", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgAddGeometryColumn, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgAddGeometryTriggers", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgAddGeometryTriggers, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgAddSpatialIndex", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgAddSpatialIndex, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointWithSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointZ", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointZ, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointZ", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointZWithSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointM", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointM", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointMWithSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointZM", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointZM, 0, 0, 0);
    sqlite3_create_function_v2 (db, "gpkgMakePointZM", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_gpkgMakePointZMWithSRID, 0, 0, 0);
    sqlite3_create_function_v2 (db, "AsGPB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_ToGPB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GeomFromGPB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GeomFromGPB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "IsValidGPB", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_IsValidGPB, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GPKG_IsAssignable", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
				fnct_GPKG_IsAssignable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CastAutomagic", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CastAutomagic, 0, 0, 0);

#endif /* end enabling GeoPackage extensions */

#ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
    if (sqlite3_libversion_number () >= 3008003)
      {
	  /* only SQLite >= 3.8.3 can suppoty WITH RECURSIVE */
	  sqlite3_create_function_v2 (db, "CreateTopoTables", 0,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopoTables, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "CreateTopology", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "CreateTopology", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "CreateTopology", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "CreateTopology", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_InitTopoGeo", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "DropTopology", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_DropTopology, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetLastTopologyException", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetLastTopologyException, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "ST_AddIsoNode", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_AddIsoNode, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_MoveIsoNode", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_MoveIsoNode, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_RemIsoNode", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_RemIsoNode, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_AddIsoEdge", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_AddIsoEdge, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_RemIsoEdge", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_RemIsoEdge, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_ModEdgeSplit", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ModEdgeSplit, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_NewEdgesSplit", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_NewEdgesSplit, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_AddEdgeModFace", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_AddEdgeModFace, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_AddEdgeNewFaces", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_AddEdgeNewFaces, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_ChangeEdgeGeom", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ChangeEdgeGeom, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_RemEdgeNewFace", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_RemEdgeNewFace, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_RemEdgeModFace", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_RemEdgeModFace, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_ModEdgeHeal", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ModEdgeHeal, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_NewEdgeHeal", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_NewEdgeHeal, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_GetFaceGeometry", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetFaceGeometry, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_GetFaceEdges", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetFaceEdges, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_ValidateTopoGeo", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_ValidateTopoGeo, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "ST_CreateTopoGeo", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_CreateTopoGeo, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetNodeByPoint", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetNodeByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetNodeByPoint", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetNodeByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetEdgeByPoint", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetEdgeByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetEdgeByPoint", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetEdgeByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetFaceByPoint", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetFaceByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "GetFaceByPoint", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_GetFaceByPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddPoint", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddPoint", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddPoint, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddLineString", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddLineString, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddLineString", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddLineString, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddLineStringNoFace", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddLineStringNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_AddLineStringNoFace", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_AddLineStringNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTable", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTable, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTable", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTable, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTable", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTable, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTable", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTable, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFace", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFace", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFace", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFace", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableNoFace,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableExt", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableExt", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableExt", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableExt", 9,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_FromGeoTableExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFaceExt", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_FromGeoTableNoFaceExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFaceExt", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_FromGeoTableNoFaceExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFaceExt", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_FromGeoTableNoFaceExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_FromGeoTableNoFaceExt", 9,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_FromGeoTableNoFaceExt, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_Polygonize", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_Polygonize, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_Polygonize", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_Polygonize, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_TopoSnap", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_TopoSnap, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_TopoSnap", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_TopoSnap, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SnappedGeoTable", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SnappedGeoTable, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SnappedGeoTable", 8,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SnappedGeoTable, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ToGeoTable", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ToGeoTable, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ToGeoTable", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ToGeoTable, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_PolyFacesList", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_PolyFacesList, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_LineEdgesList", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_LineEdgesList, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ToGeoTableGeneralize", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_ToGeoTableGeneralize, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ToGeoTableGeneralize", 7,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_ToGeoTableGeneralize, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_RemoveSmallFaces", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_RemoveSmallFaces, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_RemoveSmallFaces", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_RemoveSmallFaces, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_RemoveDanglingEdges", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_RemoveDanglingEdges,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_RemoveDanglingNodes", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_RemoveDanglingNodes,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_NewEdgeHeal", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_NewEdgeHeal, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ModEdgeHeal", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ModEdgeHeal, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_NewEdgesSplit", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_NewEdgesSplit, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_NewEdgesSplit", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_NewEdgesSplit, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ModEdgeSplit", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ModEdgeSplit, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ModEdgeSplit", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ModEdgeSplit, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_Clone", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_Clone, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SubdivideLines", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SubdivideLines, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SubdivideLines", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SubdivideLines, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_DisambiguateSegmentEdges",
				      1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_DisambiguateSegmentEdges,
				      0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_GetEdgeSeed", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_GetEdgeSeed, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_GetFaceSeed", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_GetFaceSeed, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_UpdateSeeds", 1,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_UpdateSeeds, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_UpdateSeeds", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_UpdateSeeds, 0, 0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SnapPointToSeed", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SnapPointToSeed, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_SnapLineToSeed", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_SnapLineToSeed, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_CreateTopoLayer", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_CreateTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_CreateTopoLayer", 6,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_CreateTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_InitTopoLayer", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_InitTopoLayer, 0, 0,
				      0);
	  sqlite3_create_function_v2 (db, "TopoGeo_RemoveTopoLayer", 2,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_RemoveTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ExportTopoLayer", 3,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ExportTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ExportTopoLayer", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ExportTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db, "TopoGeo_ExportTopoLayer", 5,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache, fnct_TopoGeo_ExportTopoLayer, 0,
				      0, 0);
	  sqlite3_create_function_v2 (db,
				      "TopoGeo_InsertFeatureFromTopoLayer", 4,
				      SQLITE_UTF8 | SQLITE_DETERMINISTIC,
				      cache,
				      fnct_TopoGeo_InsertFeatureFromTopoLayer,
				      0, 0, 0);
      }

    sqlite3_create_function_v2 (db, "CreateNetwork", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateNetwork", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateNetwork", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateNetwork", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "CreateNetwork", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_InitTopoNet", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_CreateNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "DropNetwork", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_DropNetwork, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLastNetworkException", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLastNetworkException, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AddIsoNetNode", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddIsoNetNode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_MoveIsoNetNode", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_MoveIsoNetNode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_RemIsoNetNode", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemIsoNetNode, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_AddLink", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_AddLink, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ChangeLinkGeom", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ChangeLinkGeom, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_RemoveLink", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_RemoveLink, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NewLogLinkSplit", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NewLogLinkSplit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ModLogLinkSplit", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ModLogLinkSplit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NewGeoLinkSplit", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NewGeoLinkSplit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ModGeoLinkSplit", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ModGeoLinkSplit, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_NewLinkHeal", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_NewLinkHeal, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ModLinkHeal", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ModLinkHeal, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_LogiNetFromTGeo", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_LogiNetFromTGeo, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SpatNetFromTGeo", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SpatNetFromTGeo, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_SpatNetFromGeom", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_SpatNetFromGeom, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ValidLogicalNet", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ValidLogicalNet, 0, 0, 0);
    sqlite3_create_function_v2 (db, "ST_ValidSpatialNet", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_ValidSpatialNet, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetNetNodeByPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetNetNodeByPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetNetNodeByPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetNetNodeByPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLinkByPoint", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLinkByPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "GetLinkByPoint", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_GetLinkByPoint, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_FromGeoTable", 4,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_FromGeoTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_ToGeoTable", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_ToGeoTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_ToGeoTable", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_ToGeoTable, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_ToGeoTableGeneralize", 6,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_ToGeoTableGeneralize, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_ToGeoTableGeneralize", 7,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_ToGeoTableGeneralize, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_Clone", 3,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_Clone, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_DisambiguateSegmentLinks", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_DisambiguateSegmentLinks, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_GetLinkSeed", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_GetLinkSeed, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_UpdateSeeds", 1,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_UpdateSeeds, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_UpdateSeeds", 2,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_UpdateSeeds, 0, 0, 0);
    sqlite3_create_function_v2 (db, "TopoNet_LineLinksList", 5,
				SQLITE_UTF8 | SQLITE_DETERMINISTIC, cache,
				fnct_TopoNet_LineLinksList, 0, 0, 0);
#endif /* end TOPOLOGY conditionals */

    return cache;
}

SPATIALITE_PRIVATE void
init_spatialite_virtualtables (void *p_db, const void *p_cache)
{
    sqlite3 *db = (sqlite3 *) p_db;

#ifndef OMIT_ICONV		/* when ICONV is disabled SHP/DBF/TXT cannot be supported */
/* initializing the VirtualShape  extension */
    virtualshape_extension_init (db);
/* initializing the VirtualDbf  extension */
    virtualdbf_extension_init (db);
/* initializing the VirtualText extension */
    virtualtext_extension_init (db);

#ifndef OMIT_FREEXL
/* initializing the VirtualXL  extension */
    virtualXL_extension_init (db);
#endif /* FreeXL enabled/disable */
#endif /* ICONV enabled/disabled */

/* initializing the VirtualNetwork  extension */
    virtualnetwork_extension_init (db);
/* initializing the MbrCache  extension */
    mbrcache_extension_init (db);
/* initializing the VirtualFDO  extension */
    virtualfdo_extension_init (db);
/* initializing the VirtualBBox  extension */
    virtualbbox_extension_init (db, p_cache);
/* initializing the VirtualSpatialIndex  extension */
    virtual_spatialindex_extension_init (db);
/* initializing the VirtualElementary  extension */
    virtual_elementary_extension_init (db);

#ifndef OMIT_GEOS		/* only if GEOS is supported */
/* initializing the VirtualRouting  extension */
    virtualrouting_extension_init (db);
#ifndef OMIT_KNN		/* only if KNN is enabled */
/* initializing the VirtualKNN  extension */
    virtual_knn_extension_init (db);
#endif /* end KNN conditional */
#endif /* end GEOS conditional */

#ifdef ENABLE_GEOPACKAGE	/* only if GeoPackage support is enabled */
/* initializing the VirtualFDO  extension */
    virtualgpkg_extension_init (db);
#endif /* end GEOPACKAGE conditional */

#ifdef ENABLE_LIBXML2		/* including LIBXML2 */
/* initializing the VirtualXPath extension */
    virtual_xpath_extension_init (db, p_cache);
#endif /* LIBXML2 enabled/disable */
}

#ifdef LOADABLE_EXTENSION	/* loadable-extension only */
SQLITE_EXTENSION_INIT1 static int
init_spatialite_extension (sqlite3 * db, char **pzErrMsg,
			   const sqlite3_api_routines * pApi)
{
    void *p_cache = spatialite_alloc_connection ();
    struct splite_internal_cache *cache =
	(struct splite_internal_cache *) p_cache;
    SQLITE_EXTENSION_INIT2 (pApi);

/* setting the POSIX locale for numeric */
    setlocale (LC_NUMERIC, "POSIX");
    *pzErrMsg = NULL;

    register_spatialite_sql_functions (db, cache);

    init_spatialite_virtualtables (db, p_cache);

/* setting a timeout handler */
    sqlite3_busy_timeout (db, 5000);

    return 0;
}
#endif

SPATIALITE_DECLARE void
spatialite_init_geos (void)
{
/* initializes GEOS (or resets to initial state - as required by RTTOPO) */
#ifndef OMIT_GEOS		/* initializing GEOS */
#ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
    initGEOS (geos_warning, geos_error);
#endif
#endif /* end GEOS  */
}

SPATIALITE_PRIVATE void
spatialite_splash_screen (int verbose)
{
    if (isatty (1))
      {
	  /* printing "hello" message only when stdout is on console */
	  if (verbose)
	    {
		spatialite_i ("SpatiaLite version ..: %s",
			      spatialite_version ());
		spatialite_i ("\tSupported Extensions:\n");
#ifndef OMIT_ICONV		/* ICONV is required by SHP/DBF/TXT */
		spatialite_i
		    ("\t- 'VirtualShape'\t[direct Shapefile access]\n");
		spatialite_i ("\t- 'VirtualDbf'\t\t[direct DBF access]\n");
#ifndef OMIT_FREEXL
		spatialite_i ("\t- 'VirtualXL'\t\t[direct XLS access]\n");
#endif /* end FreeXL conditional */
		spatialite_i ("\t- 'VirtualText'\t\t[direct CSV/TXT access]\n");
#endif /* end ICONV conditional */
		spatialite_i
		    ("\t- 'VirtualNetwork'\t[Dijkstra shortest path]\n");
		spatialite_i ("\t- 'RTree'\t\t[Spatial Index - R*Tree]\n");
		spatialite_i
		    ("\t- 'MbrCache'\t\t[Spatial Index - MBR cache]\n");
		spatialite_i
		    ("\t- 'VirtualSpatialIndex'\t[R*Tree metahandler]\n");
		spatialite_i
		    ("\t- 'VirtualElementary'\t[ElemGeoms metahandler]\n");

#ifndef OMIT_KNN		/* only if KNN is enabled */
		spatialite_i
		    ("\t- 'VirtualKNN'\t[K-Nearest Neighbors metahandler]\n");
#endif /* end KNN conditional */

#ifdef ENABLE_LIBXML2		/* VirtualXPath is supported */
		spatialite_i
		    ("\t- 'VirtualXPath'\t[XML Path Language - XPath]\n");
#endif /* end including LIBXML2 */

		spatialite_i
		    ("\t- 'VirtualFDO'\t\t[FDO-OGR interoperability]\n");

#ifdef ENABLE_GEOPACKAGE	/* VirtualGPKG is supported */
		spatialite_i
		    ("\t- 'VirtualGPKG'\t[OGC GeoPackage interoperability]\n");
#endif
		spatialite_i ("\t- 'VirtualBBox'\t\t[BoundingBox tables]\n");
		spatialite_i ("\t- 'SpatiaLite'\t\t[Spatial SQL - OGC]\n");
	    }
#ifndef OMIT_PROJ		/* PROJ.4 version */
	  if (verbose)
	      spatialite_i ("PROJ.4 version ......: %s\n", pj_get_release ());
#endif /* end including PROJ.4 */
#ifndef OMIT_GEOS		/* GEOS version */
	  if (verbose)
	      spatialite_i ("GEOS version ........: %s\n", GEOSversion ());
#endif /* end GEOS version */
#ifdef ENABLE_RTTOPO		/* RTTOPO version */
	  if (verbose)
	      spatialite_i ("RTTOPO version ......: %s\n",
			    splite_rttopo_version ());
#endif /* end RTTOPO version */
	  if (verbose)
	      spatialite_i ("TARGET CPU ..........: %s\n",
			    spatialite_target_cpu ());
      }
}

#ifndef LOADABLE_EXTENSION
SPATIALITE_DECLARE void
spatialite_init_ex (sqlite3 * db_handle, const void *p_cache, int verbose)
{
/* used when SQLite initializes as an ordinary lib */
    struct splite_internal_cache *cache =
	(struct splite_internal_cache *) p_cache;
    if (p_cache == NULL)
      {
	  spatialite_e
	      ("ERROR unable to initialize the SpatiaLite extension: NULL cache !!!\n");
	  return;
      }

/* setting the POSIX locale for numeric */
    setlocale (LC_NUMERIC, "POSIX");

    register_spatialite_sql_functions (db_handle, cache);

    init_spatialite_virtualtables (db_handle, p_cache);
    spatialite_splash_screen (verbose);

/* setting a timeout handler */
    sqlite3_busy_timeout (db_handle, 5000);
}

SPATIALITE_DECLARE void
spatialite_cleanup_ex (const void *ptr)
{
    struct splite_internal_cache *cache = (struct splite_internal_cache *) ptr;

    if (cache == NULL)
	return;
    if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
	return;

#ifdef ENABLE_RTTOPO
    gaiaResetRtTopoMsg (cache);
#endif

    free_internal_cache (cache);
    sqlite3_reset_auto_extension ();
}
#endif /* not built as loadable-extension only */

SPATIALITE_PRIVATE void
spatialite_internal_init (void *handle, const void *p_cache)
{
/* used only for internal usage */
    sqlite3 *db_handle = (sqlite3 *) handle;
    struct splite_internal_cache *cache =
	(struct splite_internal_cache *) p_cache;
    if (p_cache == NULL)
      {
	  spatialite_e
	      ("ERROR unable to initialize the SpatiaLite extension: NULL cache !!!\n");
	  return;
      }
    register_spatialite_sql_functions (db_handle, cache);
    init_spatialite_virtualtables (db_handle, p_cache);
/* setting a timeout handler */
    sqlite3_busy_timeout (db_handle, 5000);
}

SPATIALITE_PRIVATE void
spatialite_internal_cleanup (const void *ptr)
{
    struct splite_internal_cache *cache = (struct splite_internal_cache *) ptr;

    if (cache == NULL)
	return;
    if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
	return;

#ifdef ENABLE_RTTOPO
    gaiaResetRtTopoMsg (cache);
#endif

    free_internal_cache (cache);
}

#ifdef LOADABLE_EXTENSION	/* loadable-extension only */
#if !(defined _WIN32) || defined(__MINGW32__)
/* MSVC is unable to understand this declaration */
__attribute__ ((visibility ("default")))
#endif
     SPATIALITE_DECLARE int
	 sqlite3_modspatialite_init (sqlite3 * db, char **pzErrMsg,
				     const sqlite3_api_routines * pApi)
{
/* SQLite invokes this routine once when it dynamically loads the extension. */
    spatialite_initialize ();
    return init_spatialite_extension (db, pzErrMsg, pApi);
}
#endif

SPATIALITE_DECLARE sqlite3_int64
math_llabs (sqlite3_int64 value)
{
/* replacing the C99 llabs() function */
    return value < 0 ? -value : value;
}

SPATIALITE_DECLARE double
math_round (double value)
{
/* replacing the C99 round() function */
    double min = floor (value);
    if (fabs (value - min) < 0.5)
	return min;
    return min + 1.0;
}
