1 /*
2 
3  gaia_topology.c -- implementation of Topology SQL functions
4 
5  version 5.0, 2020 August 1
6 
7  Author: Sandro Furieri a.furieri@lqt.it
8 
9  -----------------------------------------------------------------------------
10 
11  Version: MPL 1.1/GPL 2.0/LGPL 2.1
12 
13  The contents of this file are subject to the Mozilla Public License Version
14  1.1 (the "License"); you may not use this file except in compliance with
15  the License. You may obtain a copy of the License at
16  http://www.mozilla.org/MPL/
17 
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22 
23 The Original Code is the SpatiaLite library
24 
25 The Initial Developer of the Original Code is Alessandro Furieri
26 
27 Portions created by the Initial Developer are Copyright (C) 2015-2021
28 the Initial Developer. All Rights Reserved.
29 
30 Contributor(s):
31 
32 Alternatively, the contents of this file may be used under the terms of
33 either the GNU General Public License Version 2 or later (the "GPL"), or
34 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 in which case the provisions of the GPL or the LGPL are applicable instead
36 of those above. If you wish to allow use of your version of this file only
37 under the terms of either the GPL or the LGPL, and not to allow others to
38 use your version of this file under the terms of the MPL, indicate your
39 decision by deleting the provisions above and replace them with the notice
40 and other provisions required by the GPL or the LGPL. If you do not delete
41 the provisions above, a recipient may use your version of this file under
42 the terms of any one of the MPL, the GPL or the LGPL.
43 
44 */
45 
46 /*
47 
48 CREDITS:
49 
50 this module has been completely funded by:
51 Regione Toscana - Settore Sistema Informativo Territoriale ed Ambientale
52 (Topology support)
53 
54 CIG: 6038019AE5
55 
56 UPDATE: supporting the new RTTOTPO 1.1 API was funded by Regione Toscana
57 CIG: 644544015A Linea A
58 
59 */
60 
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <string.h>
64 
65 #if defined(_WIN32) && !defined(__MINGW32__)
66 #include "config-msvc.h"
67 #else
68 #include "config.h"
69 #endif
70 
71 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
72 
73 #include <spatialite/sqlite.h>
74 #include <spatialite/debug.h>
75 #include <spatialite/gaiageo.h>
76 #include <spatialite/gaia_topology.h>
77 #include <spatialite/gaiaaux.h>
78 
79 #include <spatialite.h>
80 #include <spatialite_private.h>
81 
82 #include <librttopo.h>
83 
84 #include "topology_private.h"
85 
86 #ifdef _WIN32
87 #define strcasecmp	_stricmp
88 #endif /* not WIN32 */
89 
90 #define GAIA_UNUSED() if (argc || argv) argc = argc;
91 
92 struct pk_item
93 {
94 /* an helper struct for a primary key column */
95     char *name;
96     char *type;
97     int notnull;
98     int pk;
99     struct pk_item *next;
100 };
101 
102 struct pk_struct
103 {
104 /* an helper struct for cloning dustbin primary keys */
105     struct pk_item *first;
106     struct pk_item *last;
107     int count;
108 };
109 
110 static struct pk_struct *
create_pk_dictionary(void)111 create_pk_dictionary (void)
112 {
113 /* creating an empty PK dictionary */
114     struct pk_struct *pk = malloc (sizeof (struct pk_struct));
115     pk->first = NULL;
116     pk->last = NULL;
117     pk->count = 0;
118     return pk;
119 }
120 
121 static void
free_pk_dictionary(struct pk_struct * pk)122 free_pk_dictionary (struct pk_struct *pk)
123 {
124 /* memory cleanup - freeing a PK dictionary */
125     struct pk_item *pI;
126     struct pk_item *pIn;
127     if (pk == NULL)
128 	return;
129     pI = pk->first;
130     while (pI != NULL)
131       {
132 	  pIn = pI->next;
133 	  if (pI->name != NULL)
134 	      free (pI->name);
135 	  if (pI->type != NULL)
136 	      free (pI->type);
137 	  free (pI);
138 	  pI = pIn;
139       }
140     free (pk);
141 }
142 
143 static void
add_pk_column(struct pk_struct * pk,const char * name,const char * type,int notnull,int pk_pos)144 add_pk_column (struct pk_struct *pk, const char *name, const char *type,
145 	       int notnull, int pk_pos)
146 {
147 /* adding a PK column into a dictionary */
148     int len;
149     struct pk_item *pI;
150     if (pk == NULL)
151 	return;
152     if (name == NULL || type == NULL)
153 	return;
154     pI = malloc (sizeof (struct pk_item));
155     len = strlen (name);
156     pI->name = malloc (len + 1);
157     strcpy (pI->name, name);
158     len = strlen (type);
159     pI->type = malloc (len + 1);
160     strcpy (pI->type, type);
161     pI->notnull = notnull;
162     pI->pk = pk_pos;
163     pI->next = NULL;
164 /* inserting into the PK dictionary linked list */
165     if (pk->first == NULL)
166 	pk->first = pI;
167     if (pk->last != NULL)
168 	pk->last->next = pI;
169     pk->last = pI;
170     pk->count += 1;
171 }
172 
173 static struct splite_savepoint *
push_topo_savepoint(struct splite_internal_cache * cache)174 push_topo_savepoint (struct splite_internal_cache *cache)
175 {
176 /* adding a new SavePoint to the Topology stack */
177     struct splite_savepoint *p_svpt = malloc (sizeof (struct splite_savepoint));
178     p_svpt->savepoint_name = NULL;
179     p_svpt->prev = cache->last_topo_svpt;
180     p_svpt->next = NULL;
181     if (cache->first_topo_svpt == NULL)
182 	cache->first_topo_svpt = p_svpt;
183     if (cache->last_topo_svpt != NULL)
184 	cache->last_topo_svpt->next = p_svpt;
185     cache->last_topo_svpt = p_svpt;
186     return p_svpt;
187 }
188 
189 static void
pop_topo_savepoint(struct splite_internal_cache * cache)190 pop_topo_savepoint (struct splite_internal_cache *cache)
191 {
192 /* removing a SavePoint from the Topology stack */
193     struct splite_savepoint *p_svpt = cache->last_topo_svpt;
194     if (p_svpt->prev != NULL)
195 	p_svpt->prev->next = NULL;
196     cache->last_topo_svpt = p_svpt->prev;
197     if (cache->first_topo_svpt == p_svpt)
198 	cache->first_topo_svpt = NULL;
199     if (p_svpt->savepoint_name != NULL)
200 	sqlite3_free (p_svpt->savepoint_name);
201     free (p_svpt);
202 }
203 
204 SPATIALITE_PRIVATE void
start_topo_savepoint(const void * handle,const void * data)205 start_topo_savepoint (const void *handle, const void *data)
206 {
207 /* starting a new SAVEPOINT */
208     char *sql;
209     int ret;
210     char *err_msg;
211     struct splite_savepoint *p_svpt;
212     sqlite3 *sqlite = (sqlite3 *) handle;
213     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
214     if (sqlite == NULL || cache == NULL)
215 	return;
216 
217 /* creating an unique SavePoint name */
218     p_svpt = push_topo_savepoint (cache);
219     p_svpt->savepoint_name =
220 	sqlite3_mprintf ("toposvpt%04x", cache->next_topo_savepoint);
221     if (cache->next_topo_savepoint >= 0xffffffffu)
222 	cache->next_topo_savepoint = 0;
223     else
224 	cache->next_topo_savepoint += 1;
225 
226 /* starting a SavePoint */
227     sql = sqlite3_mprintf ("SAVEPOINT %s", p_svpt->savepoint_name);
228     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
229     if (ret != SQLITE_OK)
230       {
231 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
232 	  sqlite3_free (err_msg);
233       }
234     sqlite3_free (sql);
235 }
236 
237 SPATIALITE_PRIVATE void
release_topo_savepoint(const void * handle,const void * data)238 release_topo_savepoint (const void *handle, const void *data)
239 {
240 /* releasing the current SAVEPOINT (if any) */
241     char *sql;
242     int ret;
243     char *err_msg;
244     struct splite_savepoint *p_svpt;
245     sqlite3 *sqlite = (sqlite3 *) handle;
246     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
247     if (sqlite == NULL || cache == NULL)
248 	return;
249     p_svpt = cache->last_topo_svpt;
250     if (p_svpt == NULL)
251 	return;
252     if (p_svpt->savepoint_name == NULL)
253 	return;
254 
255 /* releasing the current SavePoint */
256     sql = sqlite3_mprintf ("RELEASE SAVEPOINT %s", p_svpt->savepoint_name);
257     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
258     if (ret != SQLITE_OK)
259       {
260 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
261 	  sqlite3_free (err_msg);
262       }
263     sqlite3_free (sql);
264     pop_topo_savepoint (cache);
265 }
266 
267 SPATIALITE_PRIVATE void
rollback_topo_savepoint(const void * handle,const void * data)268 rollback_topo_savepoint (const void *handle, const void *data)
269 {
270 /* rolling back the current SAVEPOINT (if any) */
271     char *sql;
272     int ret;
273     char *err_msg;
274     struct splite_savepoint *p_svpt;
275     sqlite3 *sqlite = (sqlite3 *) handle;
276     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
277     if (sqlite == NULL || cache == NULL)
278 	return;
279     p_svpt = cache->last_topo_svpt;
280     if (p_svpt == NULL)
281 	return;
282     if (p_svpt->savepoint_name == NULL)
283 	return;
284 
285 /* rolling back the current SavePoint */
286     sql = sqlite3_mprintf ("ROLLBACK TO SAVEPOINT %s", p_svpt->savepoint_name);
287     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
288     if (ret != SQLITE_OK)
289       {
290 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
291 	  sqlite3_free (err_msg);
292       }
293     sqlite3_free (sql);
294 /* releasing the current SavePoint */
295     sql = sqlite3_mprintf ("RELEASE SAVEPOINT %s", p_svpt->savepoint_name);
296     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
297     if (ret != SQLITE_OK)
298       {
299 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
300 	  sqlite3_free (err_msg);
301       }
302     sqlite3_free (sql);
303     pop_topo_savepoint (cache);
304 }
305 
306 SPATIALITE_PRIVATE void
fnctaux_GetLastTopologyException(const void * xcontext,int argc,const void * xargv)307 fnctaux_GetLastTopologyException (const void *xcontext, int argc,
308 				  const void *xargv)
309 {
310 /* SQL function:
311 / GetLastTopologyException  ( text topology-name )
312 /
313 / returns: the more recent exception raised by given Topology
314 / NULL on invalid args (or when there is no pending exception)
315 */
316     const char *topo_name;
317     GaiaTopologyAccessorPtr accessor;
318     sqlite3_context *context = (sqlite3_context *) xcontext;
319     sqlite3_value **argv = (sqlite3_value **) xargv;
320     sqlite3 *sqlite = sqlite3_context_db_handle (context);
321     struct splite_internal_cache *cache = sqlite3_user_data (context);
322     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
323     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
324 	topo_name = (const char *) sqlite3_value_text (argv[0]);
325     else
326       {
327 	  sqlite3_result_null (context);
328 	  return;
329       }
330 
331 /* attempting to get a Topology Accessor */
332     accessor = gaiaGetTopology (sqlite, cache, topo_name);
333     if (accessor == NULL)
334       {
335 	  sqlite3_result_null (context);
336 	  return;
337       }
338 
339     sqlite3_result_text (context, gaiatopo_get_last_exception (accessor), -1,
340 			 SQLITE_STATIC);
341 }
342 
343 SPATIALITE_PRIVATE void
fnctaux_CreateTopoTables(const void * xcontext,int argc,const void * argv)344 fnctaux_CreateTopoTables (const void *xcontext, int argc, const void *argv)
345 {
346 /* SQL function:
347 / CreateTopoTables()
348 /
349 / creates both TOPOLOGIES and NETWORKS tables
350 / returns 1 on success
351 / 0 on failure, -1 on invalid arguments
352 */
353     sqlite3_context *context = (sqlite3_context *) xcontext;
354     sqlite3 *sqlite = sqlite3_context_db_handle (context);
355     int topogeo;
356     int toponet;
357     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
358 
359     topogeo = do_create_topologies (sqlite);
360     toponet = do_create_networks (sqlite);
361     if (topogeo || toponet)
362 	sqlite3_result_int (context, 1);
363     else
364 	sqlite3_result_int (context, 0);
365     return;
366 }
367 
368 SPATIALITE_PRIVATE void
fnctaux_ReCreateTopoTriggers(const void * xcontext,int argc,const void * argv)369 fnctaux_ReCreateTopoTriggers (const void *xcontext, int argc, const void *argv)
370 {
371 /* SQL function:
372 / ReCreateTopoTriggers()
373 /
374 / (re)creates both TOPOLOGIES and NETWORKS triggers
375 / returns 1 on success
376 / 0 on failure, -1 on invalid arguments
377 */
378     sqlite3_context *context = (sqlite3_context *) xcontext;
379     sqlite3 *sqlite = sqlite3_context_db_handle (context);
380     int topogeo;
381     int toponet;
382     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
383 
384     drop_topologies_triggers (sqlite);
385     topogeo = do_create_topologies_triggers (sqlite);
386     drop_networks_triggers (sqlite);
387     toponet = do_create_networks_triggers (sqlite);
388     if (topogeo || toponet)
389 	sqlite3_result_int (context, 1);
390     else
391 	sqlite3_result_int (context, 0);
392     return;
393 }
394 
395 SPATIALITE_PRIVATE void
fnctaux_CreateTopology(const void * xcontext,int argc,const void * xargv)396 fnctaux_CreateTopology (const void *xcontext, int argc, const void *xargv)
397 {
398 /* SQL function:
399 / ST_InitTopoGeo ( text topology-name )
400 / CreateTopology ( text topology-name )
401 / CreateTopology ( text topology-name, int srid )
402 / CreateTopology ( text topology-name, int srid, bool hasZ )
403 / CreateTopology ( text topology-name, int srid, bool hasZ,
404 /                  double tolerance )
405 /
406 / returns: 1 on success, 0 on failure
407 / -1 on invalid args
408 / excèption on NEGATIVE tolerance
409 */
410     const char *msg;
411     int ret;
412     const char *topo_name;
413     int srid = -1;
414     int has_z = 0;
415     double tolerance = 0.0;
416     sqlite3_context *context = (sqlite3_context *) xcontext;
417     sqlite3_value **argv = (sqlite3_value **) xargv;
418     sqlite3 *sqlite = sqlite3_context_db_handle (context);
419     struct splite_internal_cache *cache = sqlite3_user_data (context);
420     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
421     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
422 	topo_name = (const char *) sqlite3_value_text (argv[0]);
423     else
424       {
425 	  sqlite3_result_int (context, -1);
426 	  return;
427       }
428     if (argc >= 2)
429       {
430 	  if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
431 	      ;
432 	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
433 	      srid = sqlite3_value_int (argv[1]);
434 	  else
435 	    {
436 		sqlite3_result_int (context, -1);
437 		return;
438 	    }
439       }
440     if (argc >= 3)
441       {
442 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
443 	      ;
444 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
445 	      has_z = sqlite3_value_int (argv[2]);
446 	  else
447 	    {
448 		sqlite3_result_int (context, -1);
449 		return;
450 	    }
451       }
452     if (argc >= 4)
453       {
454 	  if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
455 	      ;
456 	  else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
457 	      tolerance = sqlite3_value_double (argv[3]);
458 	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
459 	    {
460 		int tol = sqlite3_value_int (argv[3]);
461 		tolerance = tol;
462 	    }
463 	  else
464 	    {
465 		sqlite3_result_int (context, -1);
466 		return;
467 	    }
468       }
469 
470 /* raising an exception on NEGATIVE tolerance */
471     if (tolerance < 0.0)
472 	goto negative_tolerance;
473 
474     start_topo_savepoint (sqlite, cache);
475     ret = gaiaTopologyCreate (sqlite, topo_name, srid, tolerance, has_z);
476     if (!ret)
477 	rollback_topo_savepoint (sqlite, cache);
478     else
479 	release_topo_savepoint (sqlite, cache);
480     sqlite3_result_int (context, ret);
481     return;
482 
483   negative_tolerance:
484     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
485     sqlite3_result_error (context, msg, -1);
486     return;
487 }
488 
489 SPATIALITE_PRIVATE void
fnctaux_DropTopology(const void * xcontext,int argc,const void * xargv)490 fnctaux_DropTopology (const void *xcontext, int argc, const void *xargv)
491 {
492 /* SQL function:
493 / DropTopology ( text topology-name )
494 /
495 / returns: 1 on success, 0 on failure
496 / -1 on invalid args
497 */
498     int ret;
499     const char *topo_name;
500     sqlite3_context *context = (sqlite3_context *) xcontext;
501     sqlite3_value **argv = (sqlite3_value **) xargv;
502     sqlite3 *sqlite = sqlite3_context_db_handle (context);
503     struct splite_internal_cache *cache = sqlite3_user_data (context);
504     GaiaTopologyAccessorPtr accessor;
505     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
506     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
507 	topo_name = (const char *) sqlite3_value_text (argv[0]);
508     else
509       {
510 	  sqlite3_result_int (context, -1);
511 	  return;
512       }
513 
514     accessor = gaiaGetTopology (sqlite, cache, topo_name);
515     if (accessor != NULL)
516 	gaiaTopologyDestroy (accessor);
517 
518     start_topo_savepoint (sqlite, cache);
519     ret = gaiaTopologyDrop (sqlite, topo_name);
520     if (!ret)
521 	rollback_topo_savepoint (sqlite, cache);
522     else
523 	release_topo_savepoint (sqlite, cache);
524     sqlite3_result_int (context, ret);
525 }
526 
527 static int
check_matching_srid_dims(GaiaTopologyAccessorPtr accessor,int srid,int dims)528 check_matching_srid_dims (GaiaTopologyAccessorPtr accessor, int srid, int dims)
529 {
530 /* checking for matching SRID and DIMs */
531     struct gaia_topology *topo = (struct gaia_topology *) accessor;
532     if (topo->srid != srid)
533 	return 0;
534     if (topo->has_z)
535       {
536 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
537 	      ;
538 	  else
539 	      return 0;
540       }
541     else
542       {
543 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
544 	      return 0;
545       }
546     return 1;
547 }
548 
549 SPATIALITE_PRIVATE void
fnctaux_AddIsoNode(const void * xcontext,int argc,const void * xargv)550 fnctaux_AddIsoNode (const void *xcontext, int argc, const void *xargv)
551 {
552 /* SQL function:
553 / ST_AddIsoNode ( text topology-name, int face_id, Geometry point )
554 /
555 / returns: the ID of the inserted Node on success
556 / raises an exception on failure
557 */
558     const char *msg;
559     sqlite3_int64 ret;
560     const char *topo_name;
561     sqlite3_int64 face_id;
562     unsigned char *p_blob;
563     int n_bytes;
564     gaiaGeomCollPtr point = NULL;
565     gaiaPointPtr pt;
566     int invalid = 0;
567     GaiaTopologyAccessorPtr accessor = NULL;
568     int gpkg_amphibious = 0;
569     int gpkg_mode = 0;
570     sqlite3_context *context = (sqlite3_context *) xcontext;
571     sqlite3_value **argv = (sqlite3_value **) xargv;
572     sqlite3 *sqlite = sqlite3_context_db_handle (context);
573     struct splite_internal_cache *cache = sqlite3_user_data (context);
574     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
575     if (cache != NULL)
576       {
577 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
578 	  gpkg_mode = cache->gpkg_mode;
579       }
580     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
581 	goto null_arg;
582     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
583 	topo_name = (const char *) sqlite3_value_text (argv[0]);
584     else
585 	goto invalid_arg;
586     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
587 	face_id = -1;
588     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
589       {
590 	  face_id = sqlite3_value_int64 (argv[1]);
591 	  if (face_id <= 0)
592 	      face_id = -1;
593       }
594     else
595 	goto invalid_arg;
596     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
597 	goto null_arg;
598     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
599       {
600 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
601 	  n_bytes = sqlite3_value_bytes (argv[2]);
602       }
603     else
604 	goto invalid_arg;
605 
606 /* attempting to get a Point Geometry */
607     point =
608 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
609 				     gpkg_amphibious);
610     if (!point)
611 	goto invalid_arg;
612     if (point->FirstLinestring != NULL)
613 	invalid = 1;
614     if (point->FirstPolygon != NULL)
615 	invalid = 1;
616     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
617 	invalid = 1;
618     if (invalid)
619 	goto invalid_arg;
620 
621 /* attempting to get a Topology Accessor */
622     accessor = gaiaGetTopology (sqlite, cache, topo_name);
623     if (accessor == NULL)
624 	goto no_topo;
625     gaiatopo_reset_last_error_msg (accessor);
626     if (!check_matching_srid_dims
627 	(accessor, point->Srid, point->DimensionModel))
628 	goto invalid_geom;
629     pt = point->FirstPoint;
630 
631     start_topo_savepoint (sqlite, cache);
632     ret = gaiaAddIsoNode (accessor, face_id, pt, 0);
633     if (ret <= 0)
634 	rollback_topo_savepoint (sqlite, cache);
635     else
636 	release_topo_savepoint (sqlite, cache);
637     gaiaFreeGeomColl (point);
638     point = NULL;
639     if (ret <= 0)
640       {
641 	  msg = gaiaGetRtTopoErrorMsg (cache);
642 	  gaiatopo_set_last_error_msg (accessor, msg);
643 	  sqlite3_result_error (context, msg, -1);
644 	  return;
645       }
646     sqlite3_result_int64 (context, ret);
647     return;
648 
649   no_topo:
650     if (point != NULL)
651 	gaiaFreeGeomColl (point);
652     msg = "SQL/MM Spatial exception - invalid topology name.";
653     gaiatopo_set_last_error_msg (accessor, msg);
654     sqlite3_result_error (context, msg, -1);
655     return;
656 
657   null_arg:
658     if (point != NULL)
659 	gaiaFreeGeomColl (point);
660     msg = "SQL/MM Spatial exception - null argument.";
661     gaiatopo_set_last_error_msg (accessor, msg);
662     sqlite3_result_error (context, msg, -1);
663     return;
664 
665   invalid_arg:
666     if (point != NULL)
667 	gaiaFreeGeomColl (point);
668     msg = "SQL/MM Spatial exception - invalid argument.";
669     gaiatopo_set_last_error_msg (accessor, msg);
670     sqlite3_result_error (context, msg, -1);
671     return;
672 
673   invalid_geom:
674     if (point != NULL)
675 	gaiaFreeGeomColl (point);
676     msg =
677 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
678     gaiatopo_set_last_error_msg (accessor, msg);
679     sqlite3_result_error (context, msg, -1);
680     return;
681 }
682 
683 SPATIALITE_PRIVATE void
fnctaux_MoveIsoNode(const void * xcontext,int argc,const void * xargv)684 fnctaux_MoveIsoNode (const void *xcontext, int argc, const void *xargv)
685 {
686 /* SQL function:
687 / ST_MoveIsoNode ( text topology-name, int node_id, Geometry point )
688 /
689 / returns: TEXT (description of new location)
690 / raises an exception on failure
691 */
692     const char *msg;
693     char xid[80];
694     char *newpos = NULL;
695     int ret;
696     const char *topo_name;
697     sqlite3_int64 node_id;
698     unsigned char *p_blob;
699     int n_bytes;
700     gaiaGeomCollPtr point = NULL;
701     gaiaPointPtr pt;
702     int invalid = 0;
703     GaiaTopologyAccessorPtr accessor = NULL;
704     int gpkg_amphibious = 0;
705     int gpkg_mode = 0;
706     sqlite3_context *context = (sqlite3_context *) xcontext;
707     sqlite3_value **argv = (sqlite3_value **) xargv;
708     sqlite3 *sqlite = sqlite3_context_db_handle (context);
709     struct splite_internal_cache *cache = sqlite3_user_data (context);
710     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
711     if (cache != NULL)
712       {
713 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
714 	  gpkg_mode = cache->gpkg_mode;
715       }
716     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
717 	goto null_arg;
718     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
719 	topo_name = (const char *) sqlite3_value_text (argv[0]);
720     else
721 	goto invalid_arg;
722     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
723 	goto null_arg;
724     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
725 	node_id = sqlite3_value_int64 (argv[1]);
726     else
727 	goto invalid_arg;
728     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
729 	goto null_arg;
730     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
731       {
732 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
733 	  n_bytes = sqlite3_value_bytes (argv[2]);
734       }
735     else
736 	goto invalid_arg;
737 
738 /* attempting to get a Point Geometry */
739     point =
740 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
741 				     gpkg_amphibious);
742     if (!point)
743 	goto invalid_arg;
744     if (point->FirstLinestring != NULL)
745 	invalid = 1;
746     if (point->FirstPolygon != NULL)
747 	invalid = 1;
748     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
749 	invalid = 1;
750     if (invalid)
751 	goto invalid_arg;
752 
753 /* attempting to get a Topology Accessor */
754     accessor = gaiaGetTopology (sqlite, cache, topo_name);
755     if (accessor == NULL)
756 	goto no_topo;
757     gaiatopo_reset_last_error_msg (accessor);
758     if (!check_matching_srid_dims
759 	(accessor, point->Srid, point->DimensionModel))
760 	goto invalid_geom;
761     pt = point->FirstPoint;
762     sprintf (xid, "%lld", node_id);
763     newpos =
764 	sqlite3_mprintf ("Isolated Node %s moved to location %f,%f", xid, pt->X,
765 			 pt->Y);
766 
767     start_topo_savepoint (sqlite, cache);
768     ret = gaiaMoveIsoNode (accessor, node_id, pt);
769     if (!ret)
770 	rollback_topo_savepoint (sqlite, cache);
771     else
772 	release_topo_savepoint (sqlite, cache);
773     gaiaFreeGeomColl (point);
774     point = NULL;
775     if (!ret)
776       {
777 	  msg = gaiaGetRtTopoErrorMsg (cache);
778 	  gaiatopo_set_last_error_msg (accessor, msg);
779 	  if (newpos != NULL)
780 	      sqlite3_free (newpos);
781 	  sqlite3_result_error (context, msg, -1);
782 	  return;
783       }
784     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
785     return;
786 
787   no_topo:
788     if (newpos != NULL)
789 	sqlite3_free (newpos);
790     if (point != NULL)
791 	gaiaFreeGeomColl (point);
792     msg = "SQL/MM Spatial exception - invalid topology name.";
793     gaiatopo_set_last_error_msg (accessor, msg);
794     sqlite3_result_error (context, msg, -1);
795     return;
796 
797   null_arg:
798     if (newpos != NULL)
799 	sqlite3_free (newpos);
800     if (point != NULL)
801 	gaiaFreeGeomColl (point);
802     msg = "SQL/MM Spatial exception - null argument.";
803     gaiatopo_set_last_error_msg (accessor, msg);
804     sqlite3_result_error (context, msg, -1);
805     return;
806 
807   invalid_arg:
808     if (newpos != NULL)
809 	sqlite3_free (newpos);
810     if (point != NULL)
811 	gaiaFreeGeomColl (point);
812     msg = "SQL/MM Spatial exception - invalid argument.";
813     gaiatopo_set_last_error_msg (accessor, msg);
814     sqlite3_result_error (context, msg, -1);
815     return;
816 
817   invalid_geom:
818     if (newpos != NULL)
819 	sqlite3_free (newpos);
820     if (point != NULL)
821 	gaiaFreeGeomColl (point);
822     msg =
823 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
824     gaiatopo_set_last_error_msg (accessor, msg);
825     sqlite3_result_error (context, msg, -1);
826     return;
827 }
828 
829 SPATIALITE_PRIVATE void
fnctaux_RemIsoNode(const void * xcontext,int argc,const void * xargv)830 fnctaux_RemIsoNode (const void *xcontext, int argc, const void *xargv)
831 {
832 /* SQL function:
833 / ST_RemIsoNode ( text topology-name, int node_id )
834 /
835 / returns: TEXT (description of operation)
836 / raises an exception on failure
837 */
838     const char *msg;
839     char xid[80];
840     char *newpos = NULL;
841     int ret;
842     const char *topo_name;
843     sqlite3_int64 node_id;
844     GaiaTopologyAccessorPtr accessor = NULL;
845     sqlite3_context *context = (sqlite3_context *) xcontext;
846     sqlite3_value **argv = (sqlite3_value **) xargv;
847     sqlite3 *sqlite = sqlite3_context_db_handle (context);
848     struct splite_internal_cache *cache = sqlite3_user_data (context);
849     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
850     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
851 	goto null_arg;
852     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
853 	topo_name = (const char *) sqlite3_value_text (argv[0]);
854     else
855 	goto invalid_arg;
856     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
857 	goto null_arg;
858     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
859 	node_id = sqlite3_value_int64 (argv[1]);
860     else
861 	goto invalid_arg;
862 
863 /* attempting to get a Topology Accessor */
864     accessor = gaiaGetTopology (sqlite, cache, topo_name);
865     if (accessor == NULL)
866 	goto no_topo;
867     gaiatopo_reset_last_error_msg (accessor);
868     sprintf (xid, "%lld", node_id);
869     newpos = sqlite3_mprintf ("Isolated Node %s removed", xid);
870 
871     start_topo_savepoint (sqlite, cache);
872     ret = gaiaRemIsoNode (accessor, node_id);
873     if (!ret)
874 	rollback_topo_savepoint (sqlite, cache);
875     else
876 	release_topo_savepoint (sqlite, cache);
877     if (!ret)
878       {
879 	  msg = gaiaGetRtTopoErrorMsg (cache);
880 	  gaiatopo_set_last_error_msg (accessor, msg);
881 	  if (newpos != NULL)
882 	      sqlite3_free (newpos);
883 	  sqlite3_result_error (context, msg, -1);
884 	  return;
885       }
886     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
887     return;
888 
889   no_topo:
890     if (newpos != NULL)
891 	sqlite3_free (newpos);
892     msg = "SQL/MM Spatial exception - invalid topology name.";
893     gaiatopo_set_last_error_msg (accessor, msg);
894     sqlite3_result_error (context, msg, -1);
895     return;
896 
897   null_arg:
898     if (newpos != NULL)
899 	sqlite3_free (newpos);
900     msg = "SQL/MM Spatial exception - null argument.";
901     gaiatopo_set_last_error_msg (accessor, msg);
902     sqlite3_result_error (context, msg, -1);
903     return;
904 
905   invalid_arg:
906     if (newpos != NULL)
907 	sqlite3_free (newpos);
908     msg = "SQL/MM Spatial exception - invalid argument.";
909     gaiatopo_set_last_error_msg (accessor, msg);
910     sqlite3_result_error (context, msg, -1);
911     return;
912 }
913 
914 SPATIALITE_PRIVATE void
fnctaux_AddIsoEdge(const void * xcontext,int argc,const void * xargv)915 fnctaux_AddIsoEdge (const void *xcontext, int argc, const void *xargv)
916 {
917 /* SQL function:
918 / ST_AddIsoEdge ( text topology-name, int start_node_id, int end_node_id, Geometry linestring )
919 /
920 / returns: the ID of the inserted Edge on success, 0 on failure
921 / raises an exception on failure
922 */
923     const char *msg;
924     int ret;
925     const char *topo_name;
926     sqlite3_int64 start_node_id;
927     sqlite3_int64 end_node_id;
928     unsigned char *p_blob;
929     int n_bytes;
930     gaiaGeomCollPtr line = NULL;
931     gaiaLinestringPtr ln;
932     int invalid = 0;
933     GaiaTopologyAccessorPtr accessor = NULL;
934     int gpkg_amphibious = 0;
935     int gpkg_mode = 0;
936     sqlite3_context *context = (sqlite3_context *) xcontext;
937     sqlite3_value **argv = (sqlite3_value **) xargv;
938     sqlite3 *sqlite = sqlite3_context_db_handle (context);
939     struct splite_internal_cache *cache = sqlite3_user_data (context);
940     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
941     if (cache != NULL)
942       {
943 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
944 	  gpkg_mode = cache->gpkg_mode;
945       }
946     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
947 	goto null_arg;
948     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
949 	topo_name = (const char *) sqlite3_value_text (argv[0]);
950     else
951 	goto invalid_arg;
952     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
953 	goto null_arg;
954     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
955 	start_node_id = sqlite3_value_int64 (argv[1]);
956     else
957 	goto invalid_arg;
958     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
959 	goto null_arg;
960     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
961 	end_node_id = sqlite3_value_int64 (argv[2]);
962     else
963 	goto invalid_arg;
964     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
965 	goto null_arg;
966     else if (sqlite3_value_type (argv[3]) == SQLITE_BLOB)
967       {
968 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
969 	  n_bytes = sqlite3_value_bytes (argv[3]);
970       }
971     else
972 	goto invalid_arg;
973 
974 /* attempting to get a Linestring Geometry */
975     line =
976 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
977 				     gpkg_amphibious);
978     if (!line)
979 	goto invalid_arg;
980     if (line->FirstPoint != NULL)
981 	invalid = 1;
982     if (line->FirstPolygon != NULL)
983 	invalid = 1;
984     if (line->FirstLinestring != line->LastLinestring
985 	|| line->FirstLinestring == NULL)
986 	invalid = 1;
987     if (invalid)
988 	goto invalid_arg;
989 
990 /* attempting to get a Topology Accessor */
991     accessor = gaiaGetTopology (sqlite, cache, topo_name);
992     if (accessor == NULL)
993 	goto no_topo;
994     gaiatopo_reset_last_error_msg (accessor);
995     if (!check_matching_srid_dims (accessor, line->Srid, line->DimensionModel))
996 	goto invalid_geom;
997     ln = line->FirstLinestring;
998 
999     start_topo_savepoint (sqlite, cache);
1000     ret = gaiaAddIsoEdge (accessor, start_node_id, end_node_id, ln);
1001     if (ret <= 0)
1002 	rollback_topo_savepoint (sqlite, cache);
1003     else
1004 	release_topo_savepoint (sqlite, cache);
1005     gaiaFreeGeomColl (line);
1006     line = NULL;
1007     if (ret <= 0)
1008       {
1009 	  msg = gaiaGetRtTopoErrorMsg (cache);
1010 	  gaiatopo_set_last_error_msg (accessor, msg);
1011 	  sqlite3_result_error (context, msg, -1);
1012 	  return;
1013       }
1014     sqlite3_result_int (context, ret);
1015     return;
1016 
1017   no_topo:
1018     if (line != NULL)
1019 	gaiaFreeGeomColl (line);
1020     sqlite3_result_error (context,
1021 			  "SQL/MM Spatial exception - invalid topology name.",
1022 			  -1);
1023     return;
1024 
1025   null_arg:
1026     if (line != NULL)
1027 	gaiaFreeGeomColl (line);
1028     msg = "SQL/MM Spatial exception - null argument.";
1029     gaiatopo_set_last_error_msg (accessor, msg);
1030     sqlite3_result_error (context, msg, -1);
1031     return;
1032 
1033   invalid_arg:
1034     if (line != NULL)
1035 	gaiaFreeGeomColl (line);
1036     msg = "SQL/MM Spatial exception - invalid argument.";
1037     gaiatopo_set_last_error_msg (accessor, msg);
1038     sqlite3_result_error (context, msg, -1);
1039     return;
1040 
1041   invalid_geom:
1042     if (line != NULL)
1043 	gaiaFreeGeomColl (line);
1044     msg =
1045 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1046     gaiatopo_set_last_error_msg (accessor, msg);
1047     sqlite3_result_error (context, msg, -1);
1048     return;
1049 }
1050 
1051 SPATIALITE_PRIVATE void
fnctaux_RemIsoEdge(const void * xcontext,int argc,const void * xargv)1052 fnctaux_RemIsoEdge (const void *xcontext, int argc, const void *xargv)
1053 {
1054 /* SQL function:
1055 / ST_RemIsoEdge ( text topology-name, int edfe_id )
1056 /
1057 / returns: TEXT (description of operation)
1058 / raises an exception on failure
1059 */
1060     const char *msg;
1061     char xid[80];
1062     char *newpos = NULL;
1063     int ret;
1064     const char *topo_name;
1065     sqlite3_int64 edge_id;
1066     GaiaTopologyAccessorPtr accessor = NULL;
1067     sqlite3_context *context = (sqlite3_context *) xcontext;
1068     sqlite3_value **argv = (sqlite3_value **) xargv;
1069     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1070     struct splite_internal_cache *cache = sqlite3_user_data (context);
1071     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1072     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1073 	goto null_arg;
1074     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1075 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1076     else
1077 	goto invalid_arg;
1078     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1079 	goto null_arg;
1080     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1081 	edge_id = sqlite3_value_int64 (argv[1]);
1082     else
1083 	goto invalid_arg;
1084 
1085 /* attempting to get a Topology Accessor */
1086     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1087     if (accessor == NULL)
1088 	goto no_topo;
1089     gaiatopo_reset_last_error_msg (accessor);
1090     sprintf (xid, "%lld", edge_id);
1091     newpos = sqlite3_mprintf ("Isolated Edge %s removed", xid);
1092 
1093     start_topo_savepoint (sqlite, cache);
1094     ret = gaiaRemIsoEdge (accessor, edge_id);
1095     if (!ret)
1096 	rollback_topo_savepoint (sqlite, cache);
1097     else
1098 	release_topo_savepoint (sqlite, cache);
1099     if (!ret)
1100       {
1101 	  msg = gaiaGetRtTopoErrorMsg (cache);
1102 	  gaiatopo_set_last_error_msg (accessor, msg);
1103 	  if (newpos != NULL)
1104 	      sqlite3_free (newpos);
1105 	  sqlite3_result_error (context, msg, -1);
1106 	  return;
1107       }
1108     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
1109     return;
1110 
1111   no_topo:
1112     if (newpos != NULL)
1113 	sqlite3_free (newpos);
1114     msg = "SQL/MM Spatial exception - invalid topology name.";
1115     gaiatopo_set_last_error_msg (accessor, msg);
1116     sqlite3_result_error (context, msg, -1);
1117     return;
1118 
1119   null_arg:
1120     if (newpos != NULL)
1121 	sqlite3_free (newpos);
1122     msg = "SQL/MM Spatial exception - null argument.";
1123     gaiatopo_set_last_error_msg (accessor, msg);
1124     sqlite3_result_error (context, msg, -1);
1125     return;
1126 
1127   invalid_arg:
1128     if (newpos != NULL)
1129 	sqlite3_free (newpos);
1130     msg = "SQL/MM Spatial exception - invalid argument.";
1131     gaiatopo_set_last_error_msg (accessor, msg);
1132     sqlite3_result_error (context, msg, -1);
1133     return;
1134 }
1135 
1136 SPATIALITE_PRIVATE void
fnctaux_RemEdgeModFace(const void * xcontext,int argc,const void * xargv)1137 fnctaux_RemEdgeModFace (const void *xcontext, int argc, const void *xargv)
1138 {
1139 /* SQL function:
1140 / ST_RemEdgeModFace ( text topology-name, int edge_id )
1141 /
1142 / returns: ID of the Face that takes up the space previously occupied by the removed edge
1143 / raises an exception on failure
1144 */
1145     const char *msg;
1146     sqlite3_int64 ret;
1147     const char *topo_name;
1148     sqlite3_int64 edge_id;
1149     GaiaTopologyAccessorPtr accessor = NULL;
1150     sqlite3_context *context = (sqlite3_context *) xcontext;
1151     sqlite3_value **argv = (sqlite3_value **) xargv;
1152     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1153     struct splite_internal_cache *cache = sqlite3_user_data (context);
1154     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1155     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1156 	goto null_arg;
1157     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1158 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1159     else
1160 	goto invalid_arg;
1161     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1162 	goto null_arg;
1163     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1164 	edge_id = sqlite3_value_int64 (argv[1]);
1165     else
1166 	goto invalid_arg;
1167 
1168 /* attempting to get a Topology Accessor */
1169     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1170     if (accessor == NULL)
1171 	goto no_topo;
1172     gaiatopo_reset_last_error_msg (accessor);
1173 
1174     start_topo_savepoint (sqlite, cache);
1175     ret = gaiaRemEdgeModFace (accessor, edge_id);
1176     if (ret < 0)
1177 	rollback_topo_savepoint (sqlite, cache);
1178     else
1179 	release_topo_savepoint (sqlite, cache);
1180     if (ret < 0)
1181       {
1182 	  msg = gaiaGetRtTopoErrorMsg (cache);
1183 	  gaiatopo_set_last_error_msg (accessor, msg);
1184 	  sqlite3_result_error (context, msg, -1);
1185 	  return;
1186       }
1187     sqlite3_result_int64 (context, ret);
1188     return;
1189 
1190   no_topo:
1191     msg = "SQL/MM Spatial exception - invalid topology name.";
1192     gaiatopo_set_last_error_msg (accessor, msg);
1193     sqlite3_result_error (context, msg, -1);
1194     return;
1195 
1196   null_arg:
1197     msg = "SQL/MM Spatial exception - null argument.";
1198     gaiatopo_set_last_error_msg (accessor, msg);
1199     sqlite3_result_error (context, msg, -1);
1200     return;
1201 
1202   invalid_arg:
1203     msg = "SQL/MM Spatial exception - invalid argument.";
1204     gaiatopo_set_last_error_msg (accessor, msg);
1205     sqlite3_result_error (context, msg, -1);
1206     return;
1207 }
1208 
1209 SPATIALITE_PRIVATE void
fnctaux_RemEdgeNewFace(const void * xcontext,int argc,const void * xargv)1210 fnctaux_RemEdgeNewFace (const void *xcontext, int argc, const void *xargv)
1211 {
1212 /* SQL function:
1213 / ST_RemEdgeNewFace ( text topology-name, int edge_id )
1214 /
1215 / returns: ID of the created Face
1216 / raises an exception on failure
1217 */
1218     const char *msg;
1219     sqlite3_int64 ret;
1220     const char *topo_name;
1221     sqlite3_int64 edge_id;
1222     GaiaTopologyAccessorPtr accessor = NULL;
1223     sqlite3_context *context = (sqlite3_context *) xcontext;
1224     sqlite3_value **argv = (sqlite3_value **) xargv;
1225     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1226     struct splite_internal_cache *cache = sqlite3_user_data (context);
1227     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1228     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1229 	goto null_arg;
1230     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1231 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1232     else
1233 	goto invalid_arg;
1234     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1235 	goto null_arg;
1236     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1237 	edge_id = sqlite3_value_int64 (argv[1]);
1238     else
1239 	goto invalid_arg;
1240 
1241 /* attempting to get a Topology Accessor */
1242     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1243     if (accessor == NULL)
1244 	goto no_topo;
1245     gaiatopo_reset_last_error_msg (accessor);
1246 
1247     start_topo_savepoint (sqlite, cache);
1248     ret = gaiaRemEdgeNewFace (accessor, edge_id);
1249     if (ret < 0)
1250 	rollback_topo_savepoint (sqlite, cache);
1251     else
1252 	release_topo_savepoint (sqlite, cache);
1253     if (ret < 0)
1254       {
1255 	  msg = gaiaGetRtTopoErrorMsg (cache);
1256 	  gaiatopo_set_last_error_msg (accessor, msg);
1257 	  sqlite3_result_error (context, msg, -1);
1258 	  return;
1259       }
1260     sqlite3_result_int64 (context, ret);
1261     return;
1262 
1263   no_topo:
1264     msg = "SQL/MM Spatial exception - invalid topology name.";
1265     gaiatopo_set_last_error_msg (accessor, msg);
1266     sqlite3_result_error (context, msg, -1);
1267     return;
1268 
1269   null_arg:
1270     msg = "SQL/MM Spatial exception - null argument.";
1271     gaiatopo_set_last_error_msg (accessor, msg);
1272     sqlite3_result_error (context, msg, -1);
1273     return;
1274 
1275   invalid_arg:
1276     msg = "SQL/MM Spatial exception - invalid argument.";
1277     gaiatopo_set_last_error_msg (accessor, msg);
1278     sqlite3_result_error (context, msg, -1);
1279     return;
1280 }
1281 
1282 SPATIALITE_PRIVATE void
fnctaux_ChangeEdgeGeom(const void * xcontext,int argc,const void * xargv)1283 fnctaux_ChangeEdgeGeom (const void *xcontext, int argc, const void *xargv)
1284 {
1285 /* SQL function:
1286 / ST_ChangeEdgeGeom ( text topology-name, int edge_id, Geometry linestring )
1287 /
1288 / returns: TEXT (description of operation)
1289 / raises an exception on failure
1290 */
1291     const char *msg;
1292     char xid[80];
1293     char *newpos = NULL;
1294     int ret;
1295     const char *topo_name;
1296     sqlite3_int64 edge_id;
1297     unsigned char *p_blob;
1298     int n_bytes;
1299     gaiaGeomCollPtr line = NULL;
1300     gaiaLinestringPtr ln;
1301     int invalid = 0;
1302     GaiaTopologyAccessorPtr accessor = NULL;
1303     int gpkg_amphibious = 0;
1304     int gpkg_mode = 0;
1305     sqlite3_context *context = (sqlite3_context *) xcontext;
1306     sqlite3_value **argv = (sqlite3_value **) xargv;
1307     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1308     struct splite_internal_cache *cache = sqlite3_user_data (context);
1309     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1310     if (cache != NULL)
1311       {
1312 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1313 	  gpkg_mode = cache->gpkg_mode;
1314       }
1315     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1316 	goto null_arg;
1317     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1318 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1319     else
1320 	goto invalid_arg;
1321     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1322 	goto null_arg;
1323     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1324 	edge_id = sqlite3_value_int64 (argv[1]);
1325     else
1326 	goto invalid_arg;
1327     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1328 	goto null_arg;
1329     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1330       {
1331 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1332 	  n_bytes = sqlite3_value_bytes (argv[2]);
1333       }
1334     else
1335 	goto invalid_arg;
1336 
1337 /* attempting to get a Linestring Geometry */
1338     line =
1339 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1340 				     gpkg_amphibious);
1341     if (!line)
1342 	goto invalid_arg;
1343     if (line->FirstPoint != NULL)
1344 	invalid = 1;
1345     if (line->FirstPolygon != NULL)
1346 	invalid = 1;
1347     if (line->FirstLinestring != line->LastLinestring
1348 	|| line->FirstLinestring == NULL)
1349 	invalid = 1;
1350     if (invalid)
1351 	goto invalid_arg;
1352 
1353 /* attempting to get a Topology Accessor */
1354     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1355     if (accessor == NULL)
1356 	goto no_topo;
1357     gaiatopo_reset_last_error_msg (accessor);
1358     if (!check_matching_srid_dims (accessor, line->Srid, line->DimensionModel))
1359 	goto invalid_geom;
1360     ln = line->FirstLinestring;
1361     sprintf (xid, "%lld", edge_id);
1362     newpos = sqlite3_mprintf ("Edge %s changed", xid);
1363 
1364     start_topo_savepoint (sqlite, cache);
1365     ret = gaiaChangeEdgeGeom (accessor, edge_id, ln);
1366     if (!ret)
1367 	rollback_topo_savepoint (sqlite, cache);
1368     else
1369 	release_topo_savepoint (sqlite, cache);
1370     gaiaFreeGeomColl (line);
1371     line = NULL;
1372     if (!ret)
1373       {
1374 	  msg = gaiaGetRtTopoErrorMsg (cache);
1375 	  gaiatopo_set_last_error_msg (accessor, msg);
1376 	  if (newpos != NULL)
1377 	      sqlite3_free (newpos);
1378 	  sqlite3_result_error (context, msg, -1);
1379 	  return;
1380       }
1381     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
1382     return;
1383 
1384   no_topo:
1385     if (newpos != NULL)
1386 	sqlite3_free (newpos);
1387     if (line != NULL)
1388 	gaiaFreeGeomColl (line);
1389     msg = "SQL/MM Spatial exception - invalid topology name.";
1390     gaiatopo_set_last_error_msg (accessor, msg);
1391     sqlite3_result_error (context, msg, -1);
1392     return;
1393 
1394   null_arg:
1395     if (newpos != NULL)
1396 	sqlite3_free (newpos);
1397     if (line != NULL)
1398 	gaiaFreeGeomColl (line);
1399     msg = "SQL/MM Spatial exception - null argument.";
1400     gaiatopo_set_last_error_msg (accessor, msg);
1401     sqlite3_result_error (context, msg, -1);
1402     return;
1403 
1404   invalid_arg:
1405     if (newpos != NULL)
1406 	sqlite3_free (newpos);
1407     if (line != NULL)
1408 	gaiaFreeGeomColl (line);
1409     msg = "SQL/MM Spatial exception - invalid argument.";
1410     gaiatopo_set_last_error_msg (accessor, msg);
1411     sqlite3_result_error (context, msg, -1);
1412     return;
1413 
1414   invalid_geom:
1415     if (newpos != NULL)
1416 	sqlite3_free (newpos);
1417     if (line != NULL)
1418 	gaiaFreeGeomColl (line);
1419     msg =
1420 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1421     gaiatopo_set_last_error_msg (accessor, msg);
1422     sqlite3_result_error (context, msg, -1);
1423     return;
1424 }
1425 
1426 SPATIALITE_PRIVATE void
fnctaux_ModEdgeSplit(const void * xcontext,int argc,const void * xargv)1427 fnctaux_ModEdgeSplit (const void *xcontext, int argc, const void *xargv)
1428 {
1429 /* SQL function:
1430 / ST_ModEdgeSplit ( text topology-name, int edge_id, Geometry point )
1431 /
1432 / returns: the ID of the inserted Node on success
1433 / raises an exception on failure
1434 */
1435     const char *msg;
1436     sqlite3_int64 ret;
1437     const char *topo_name;
1438     sqlite3_int64 edge_id;
1439     unsigned char *p_blob;
1440     int n_bytes;
1441     gaiaGeomCollPtr point = NULL;
1442     gaiaPointPtr pt;
1443     int invalid = 0;
1444     GaiaTopologyAccessorPtr accessor = NULL;
1445     int gpkg_amphibious = 0;
1446     int gpkg_mode = 0;
1447     sqlite3_context *context = (sqlite3_context *) xcontext;
1448     sqlite3_value **argv = (sqlite3_value **) xargv;
1449     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1450     struct splite_internal_cache *cache = sqlite3_user_data (context);
1451     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1452     if (cache != NULL)
1453       {
1454 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1455 	  gpkg_mode = cache->gpkg_mode;
1456       }
1457     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1458 	goto null_arg;
1459     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1460 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1461     else
1462 	goto invalid_arg;
1463     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1464 	goto null_arg;
1465     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1466 	edge_id = sqlite3_value_int64 (argv[1]);
1467     else
1468 	goto invalid_arg;
1469     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1470 	goto null_arg;
1471     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1472       {
1473 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1474 	  n_bytes = sqlite3_value_bytes (argv[2]);
1475       }
1476     else
1477 	goto invalid_arg;
1478 
1479 /* attempting to get a Point Geometry */
1480     point =
1481 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1482 				     gpkg_amphibious);
1483     if (!point)
1484 	goto invalid_arg;
1485     if (point->FirstLinestring != NULL)
1486 	invalid = 1;
1487     if (point->FirstPolygon != NULL)
1488 	invalid = 1;
1489     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
1490 	invalid = 1;
1491     if (invalid)
1492 	goto invalid_arg;
1493 
1494 /* attempting to get a Topology Accessor */
1495     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1496     if (accessor == NULL)
1497 	goto no_topo;
1498     gaiatopo_reset_last_error_msg (accessor);
1499     if (!check_matching_srid_dims
1500 	(accessor, point->Srid, point->DimensionModel))
1501 	goto invalid_geom;
1502     pt = point->FirstPoint;
1503 
1504     start_topo_savepoint (sqlite, cache);
1505     ret = gaiaModEdgeSplit (accessor, edge_id, pt, 0);
1506     if (ret <= 0)
1507 	rollback_topo_savepoint (sqlite, cache);
1508     else
1509 	release_topo_savepoint (sqlite, cache);
1510     gaiaFreeGeomColl (point);
1511     point = NULL;
1512     if (ret <= 0)
1513       {
1514 	  msg = gaiaGetRtTopoErrorMsg (cache);
1515 	  gaiatopo_set_last_error_msg (accessor, msg);
1516 	  sqlite3_result_error (context, msg, -1);
1517 	  return;
1518       }
1519     sqlite3_result_int (context, ret);
1520     return;
1521 
1522   no_topo:
1523     if (point != NULL)
1524 	gaiaFreeGeomColl (point);
1525     msg = "SQL/MM Spatial exception - invalid topology name.";
1526     gaiatopo_set_last_error_msg (accessor, msg);
1527     sqlite3_result_error (context, msg, -1);
1528     return;
1529 
1530   null_arg:
1531     if (point != NULL)
1532 	gaiaFreeGeomColl (point);
1533     msg = "SQL/MM Spatial exception - null argument.";
1534     gaiatopo_set_last_error_msg (accessor, msg);
1535     sqlite3_result_error (context, msg, -1);
1536     return;
1537 
1538   invalid_arg:
1539     if (point != NULL)
1540 	gaiaFreeGeomColl (point);
1541     msg = "SQL/MM Spatial exception - invalid argument.";
1542     gaiatopo_set_last_error_msg (accessor, msg);
1543     sqlite3_result_error (context, msg, -1);
1544     return;
1545 
1546   invalid_geom:
1547     if (point != NULL)
1548 	gaiaFreeGeomColl (point);
1549     msg =
1550 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1551     gaiatopo_set_last_error_msg (accessor, msg);
1552     sqlite3_result_error (context, msg, -1);
1553     return;
1554 }
1555 
1556 SPATIALITE_PRIVATE void
fnctaux_NewEdgesSplit(const void * xcontext,int argc,const void * xargv)1557 fnctaux_NewEdgesSplit (const void *xcontext, int argc, const void *xargv)
1558 {
1559 /* SQL function:
1560 / ST_NewEdgesSplit ( text topology-name, int edge_id, Geometry point )
1561 /
1562 / returns: the ID of the inserted Node on success
1563 / raises an exception on failure
1564 */
1565     const char *msg;
1566     sqlite3_int64 ret;
1567     const char *topo_name;
1568     sqlite3_int64 edge_id;
1569     unsigned char *p_blob;
1570     int n_bytes;
1571     gaiaGeomCollPtr point = NULL;
1572     gaiaPointPtr pt;
1573     int invalid = 0;
1574     GaiaTopologyAccessorPtr accessor = NULL;
1575     int gpkg_amphibious = 0;
1576     int gpkg_mode = 0;
1577     sqlite3_context *context = (sqlite3_context *) xcontext;
1578     sqlite3_value **argv = (sqlite3_value **) xargv;
1579     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1580     struct splite_internal_cache *cache = sqlite3_user_data (context);
1581     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1582     if (cache != NULL)
1583       {
1584 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1585 	  gpkg_mode = cache->gpkg_mode;
1586       }
1587     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1588 	goto null_arg;
1589     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1590 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1591     else
1592 	goto invalid_arg;
1593     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1594 	goto null_arg;
1595     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1596 	edge_id = sqlite3_value_int64 (argv[1]);
1597     else
1598 	goto invalid_arg;
1599     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1600 	goto null_arg;
1601     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1602       {
1603 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1604 	  n_bytes = sqlite3_value_bytes (argv[2]);
1605       }
1606     else
1607 	goto invalid_arg;
1608 
1609 /* attempting to get a Point Geometry */
1610     point =
1611 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1612 				     gpkg_amphibious);
1613     if (!point)
1614       {
1615 	  goto invalid_arg;
1616 	  return;
1617       }
1618     if (point->FirstLinestring != NULL)
1619 	invalid = 1;
1620     if (point->FirstPolygon != NULL)
1621 	invalid = 1;
1622     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
1623 	invalid = 1;
1624     if (invalid)
1625 	goto invalid_arg;
1626 
1627 /* attempting to get a Topology Accessor */
1628     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1629     if (accessor == NULL)
1630 	goto no_topo;
1631     gaiatopo_reset_last_error_msg (accessor);
1632     if (!check_matching_srid_dims
1633 	(accessor, point->Srid, point->DimensionModel))
1634 	goto invalid_geom;
1635     pt = point->FirstPoint;
1636 
1637     start_topo_savepoint (sqlite, cache);
1638     ret = gaiaNewEdgesSplit (accessor, edge_id, pt, 0);
1639     if (ret <= 0)
1640 	rollback_topo_savepoint (sqlite, cache);
1641     else
1642 	release_topo_savepoint (sqlite, cache);
1643     gaiaFreeGeomColl (point);
1644     point = NULL;
1645     if (ret <= 0)
1646       {
1647 	  msg = gaiaGetRtTopoErrorMsg (cache);
1648 	  gaiatopo_set_last_error_msg (accessor, msg);
1649 	  sqlite3_result_error (context, msg, -1);
1650 	  return;
1651       }
1652     sqlite3_result_int (context, ret);
1653     return;
1654 
1655   no_topo:
1656     if (point != NULL)
1657 	gaiaFreeGeomColl (point);
1658     msg = "SQL/MM Spatial exception - invalid topology name.";
1659     gaiatopo_set_last_error_msg (accessor, msg);
1660     sqlite3_result_error (context, msg, -1);
1661     return;
1662 
1663   null_arg:
1664     if (point != NULL)
1665 	gaiaFreeGeomColl (point);
1666     msg = "SQL/MM Spatial exception - null argument.";
1667     gaiatopo_set_last_error_msg (accessor, msg);
1668     sqlite3_result_error (context, msg, -1);
1669     return;
1670 
1671   invalid_arg:
1672     if (point != NULL)
1673 	gaiaFreeGeomColl (point);
1674     msg = "SQL/MM Spatial exception - invalid argument.";
1675     gaiatopo_set_last_error_msg (accessor, msg);
1676     sqlite3_result_error (context, msg, -1);
1677     return;
1678 
1679   invalid_geom:
1680     if (point != NULL)
1681 	gaiaFreeGeomColl (point);
1682     msg =
1683 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1684     gaiatopo_set_last_error_msg (accessor, msg);
1685     sqlite3_result_error (context, msg, -1);
1686     return;
1687 }
1688 
1689 SPATIALITE_PRIVATE void
fnctaux_AddEdgeModFace(const void * xcontext,int argc,const void * xargv)1690 fnctaux_AddEdgeModFace (const void *xcontext, int argc, const void *xargv)
1691 {
1692 /* SQL function:
1693 / ST_AddEdgeModFace ( text topology-name, int start_node_id, int end_node_id, Geometry linestring )
1694 /
1695 / returns: the ID of the inserted Edge on success
1696 / raises an exception on failure
1697 */
1698     const char *msg;
1699     sqlite3_int64 ret;
1700     const char *topo_name;
1701     sqlite3_int64 start_node_id;
1702     sqlite3_int64 end_node_id;
1703     unsigned char *p_blob;
1704     int n_bytes;
1705     gaiaGeomCollPtr line = NULL;
1706     gaiaLinestringPtr ln;
1707     int invalid = 0;
1708     GaiaTopologyAccessorPtr accessor = NULL;
1709     int gpkg_amphibious = 0;
1710     int gpkg_mode = 0;
1711     sqlite3_context *context = (sqlite3_context *) xcontext;
1712     sqlite3_value **argv = (sqlite3_value **) xargv;
1713     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1714     struct splite_internal_cache *cache = sqlite3_user_data (context);
1715     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1716     if (cache != NULL)
1717       {
1718 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1719 	  gpkg_mode = cache->gpkg_mode;
1720       }
1721     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1722 	goto null_arg;
1723     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1724 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1725     else
1726 	goto invalid_arg;
1727     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1728 	goto null_arg;
1729     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1730 	start_node_id = sqlite3_value_int64 (argv[1]);
1731     else
1732 	goto invalid_arg;
1733     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1734 	goto null_arg;
1735     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
1736 	end_node_id = sqlite3_value_int64 (argv[2]);
1737     else
1738 	goto invalid_arg;
1739     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
1740 	goto null_arg;
1741     else if (sqlite3_value_type (argv[3]) == SQLITE_BLOB)
1742       {
1743 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
1744 	  n_bytes = sqlite3_value_bytes (argv[3]);
1745       }
1746     else
1747 	goto invalid_arg;
1748 
1749 /* attempting to get a Linestring Geometry */
1750     line =
1751 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1752 				     gpkg_amphibious);
1753     if (!line)
1754 	goto invalid_arg;
1755     if (line->FirstPoint != NULL)
1756 	invalid = 1;
1757     if (line->FirstPolygon != NULL)
1758 	invalid = 1;
1759     if (line->FirstLinestring != line->LastLinestring
1760 	|| line->FirstLinestring == NULL)
1761 	invalid = 1;
1762     if (invalid)
1763 	goto invalid_arg;
1764 
1765 /* attempting to get a Topology Accessor */
1766     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1767     if (accessor == NULL)
1768 	goto no_topo;
1769     gaiatopo_reset_last_error_msg (accessor);
1770     if (!check_matching_srid_dims (accessor, line->Srid, line->DimensionModel))
1771 	goto invalid_geom;
1772     ln = line->FirstLinestring;
1773 
1774     start_topo_savepoint (sqlite, cache);
1775     ret = gaiaAddEdgeModFace (accessor, start_node_id, end_node_id, ln, 0);
1776     if (ret <= 0)
1777 	rollback_topo_savepoint (sqlite, cache);
1778     else
1779 	release_topo_savepoint (sqlite, cache);
1780     gaiaFreeGeomColl (line);
1781     line = NULL;
1782     if (ret <= 0)
1783       {
1784 	  msg = gaiaGetRtTopoErrorMsg (cache);
1785 	  gaiatopo_set_last_error_msg (accessor, msg);
1786 	  sqlite3_result_error (context, msg, -1);
1787 	  return;
1788       }
1789     sqlite3_result_int (context, ret);
1790     return;
1791 
1792   no_topo:
1793     if (line != NULL)
1794 	gaiaFreeGeomColl (line);
1795     msg = "SQL/MM Spatial exception - invalid topology name.";
1796     gaiatopo_set_last_error_msg (accessor, msg);
1797     sqlite3_result_error (context, msg, -1);
1798     return;
1799 
1800   null_arg:
1801     if (line != NULL)
1802 	gaiaFreeGeomColl (line);
1803     msg = "SQL/MM Spatial exception - null argument.";
1804     gaiatopo_set_last_error_msg (accessor, msg);
1805     sqlite3_result_error (context, msg, -1);
1806     return;
1807 
1808   invalid_arg:
1809     if (line != NULL)
1810 	gaiaFreeGeomColl (line);
1811     msg = "SQL/MM Spatial exception - invalid argument.";
1812     gaiatopo_set_last_error_msg (accessor, msg);
1813     sqlite3_result_error (context, msg, -1);
1814     return;
1815 
1816   invalid_geom:
1817     if (line != NULL)
1818 	gaiaFreeGeomColl (line);
1819     msg =
1820 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1821     gaiatopo_set_last_error_msg (accessor, msg);
1822     sqlite3_result_error (context, msg, -1);
1823     return;
1824 }
1825 
1826 SPATIALITE_PRIVATE void
fnctaux_AddEdgeNewFaces(const void * xcontext,int argc,const void * xargv)1827 fnctaux_AddEdgeNewFaces (const void *xcontext, int argc, const void *xargv)
1828 {
1829 /* SQL function:
1830 / ST_AddEdgeNewFaces ( text topology-name, int start_node_id, int end_node_id, Geometry linestring )
1831 /
1832 / returns: the ID of the inserted Edge on success
1833 / raises an exception on failure
1834 */
1835     const char *msg;
1836     sqlite3_int64 ret;
1837     const char *topo_name;
1838     sqlite3_int64 start_node_id;
1839     sqlite3_int64 end_node_id;
1840     unsigned char *p_blob;
1841     int n_bytes;
1842     gaiaGeomCollPtr line = NULL;
1843     gaiaLinestringPtr ln;
1844     int invalid = 0;
1845     GaiaTopologyAccessorPtr accessor = NULL;
1846     int gpkg_amphibious = 0;
1847     int gpkg_mode = 0;
1848     sqlite3_context *context = (sqlite3_context *) xcontext;
1849     sqlite3_value **argv = (sqlite3_value **) xargv;
1850     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1851     struct splite_internal_cache *cache = sqlite3_user_data (context);
1852     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1853     if (cache != NULL)
1854       {
1855 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1856 	  gpkg_mode = cache->gpkg_mode;
1857       }
1858     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1859 	goto null_arg;
1860     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1861 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1862     else
1863 	goto invalid_arg;
1864     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1865 	goto null_arg;
1866     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1867 	start_node_id = sqlite3_value_int64 (argv[1]);
1868     else
1869 	goto invalid_arg;
1870     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1871 	goto null_arg;
1872     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
1873 	end_node_id = sqlite3_value_int64 (argv[2]);
1874     else
1875 	goto invalid_arg;
1876     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
1877 	goto null_arg;
1878     else if (sqlite3_value_type (argv[3]) == SQLITE_BLOB)
1879       {
1880 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
1881 	  n_bytes = sqlite3_value_bytes (argv[3]);
1882       }
1883     else
1884 	goto invalid_arg;
1885 
1886 /* attempting to get a Linestring Geometry */
1887     line =
1888 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1889 				     gpkg_amphibious);
1890     if (!line)
1891 	goto invalid_arg;
1892     if (line->FirstPoint != NULL)
1893 	invalid = 1;
1894     if (line->FirstPolygon != NULL)
1895 	invalid = 1;
1896     if (line->FirstLinestring != line->LastLinestring
1897 	|| line->FirstLinestring == NULL)
1898 	invalid = 1;
1899     if (invalid)
1900 	goto invalid_arg;
1901 
1902 /* attempting to get a Topology Accessor */
1903     accessor = gaiaGetTopology (sqlite, cache, topo_name);
1904     if (accessor == NULL)
1905 	goto no_topo;
1906     gaiatopo_reset_last_error_msg (accessor);
1907     if (!check_matching_srid_dims (accessor, line->Srid, line->DimensionModel))
1908 	goto invalid_geom;
1909     ln = line->FirstLinestring;
1910 
1911     start_topo_savepoint (sqlite, cache);
1912     ret = gaiaAddEdgeNewFaces (accessor, start_node_id, end_node_id, ln, 0);
1913     if (ret <= 0)
1914 	rollback_topo_savepoint (sqlite, cache);
1915     else
1916 	release_topo_savepoint (sqlite, cache);
1917     gaiaFreeGeomColl (line);
1918     line = NULL;
1919     if (ret <= 0)
1920       {
1921 	  msg = gaiaGetRtTopoErrorMsg (cache);
1922 	  gaiatopo_set_last_error_msg (accessor, msg);
1923 	  sqlite3_result_error (context, msg, -1);
1924 	  return;
1925       }
1926     sqlite3_result_int (context, ret);
1927     return;
1928 
1929   no_topo:
1930     if (line != NULL)
1931 	gaiaFreeGeomColl (line);
1932     msg = "SQL/MM Spatial exception - invalid topology name.";
1933     gaiatopo_set_last_error_msg (accessor, msg);
1934     sqlite3_result_error (context, msg, -1);
1935     return;
1936 
1937   null_arg:
1938     if (line != NULL)
1939 	gaiaFreeGeomColl (line);
1940     msg = "SQL/MM Spatial exception - null argument.";
1941     gaiatopo_set_last_error_msg (accessor, msg);
1942     sqlite3_result_error (context, msg, -1);
1943     return;
1944 
1945   invalid_arg:
1946     if (line != NULL)
1947 	gaiaFreeGeomColl (line);
1948     msg = "SQL/MM Spatial exception - invalid argument.";
1949     gaiatopo_set_last_error_msg (accessor, msg);
1950     sqlite3_result_error (context, msg, -1);
1951     return;
1952 
1953   invalid_geom:
1954     if (line != NULL)
1955 	gaiaFreeGeomColl (line);
1956     msg =
1957 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
1958     gaiatopo_set_last_error_msg (accessor, msg);
1959     sqlite3_result_error (context, msg, -1);
1960     return;
1961 }
1962 
1963 SPATIALITE_PRIVATE void
fnctaux_ModEdgeHeal(const void * xcontext,int argc,const void * xargv)1964 fnctaux_ModEdgeHeal (const void *xcontext, int argc, const void *xargv)
1965 {
1966 /* SQL function:
1967 / ST_ModEdgeHeal ( text topology-name, int edge_id1, int edge_id2 )
1968 /
1969 / returns: ID of the removed Node
1970 / raises an exception on failure
1971 */
1972     const char *msg;
1973     sqlite3_int64 ret;
1974     const char *topo_name;
1975     sqlite3_int64 edge_id1;
1976     sqlite3_int64 edge_id2;
1977     GaiaTopologyAccessorPtr accessor = NULL;
1978     sqlite3_context *context = (sqlite3_context *) xcontext;
1979     sqlite3_value **argv = (sqlite3_value **) xargv;
1980     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1981     struct splite_internal_cache *cache = sqlite3_user_data (context);
1982     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1983     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1984 	goto null_arg;
1985     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1986 	topo_name = (const char *) sqlite3_value_text (argv[0]);
1987     else
1988 	goto invalid_arg;
1989     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1990 	goto null_arg;
1991     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1992 	edge_id1 = sqlite3_value_int64 (argv[1]);
1993     else
1994 	goto invalid_arg;
1995     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1996 	goto null_arg;
1997     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
1998 	edge_id2 = sqlite3_value_int64 (argv[2]);
1999     else
2000 	goto invalid_arg;
2001 
2002 /* attempting to get a Topology Accessor */
2003     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2004     if (accessor == NULL)
2005 	goto no_topo;
2006     gaiatopo_reset_last_error_msg (accessor);
2007 
2008     start_topo_savepoint (sqlite, cache);
2009     ret = gaiaModEdgeHeal (accessor, edge_id1, edge_id2);
2010     if (ret < 0)
2011 	rollback_topo_savepoint (sqlite, cache);
2012     else
2013 	release_topo_savepoint (sqlite, cache);
2014     if (ret < 0)
2015       {
2016 	  msg = gaiaGetRtTopoErrorMsg (cache);
2017 	  gaiatopo_set_last_error_msg (accessor, msg);
2018 	  sqlite3_result_error (context, msg, -1);
2019 	  return;
2020       }
2021     sqlite3_result_int64 (context, ret);
2022     return;
2023 
2024   no_topo:
2025     msg = "SQL/MM Spatial exception - invalid topology name.";
2026     gaiatopo_set_last_error_msg (accessor, msg);
2027     sqlite3_result_error (context, msg, -1);
2028     return;
2029 
2030   null_arg:
2031     msg = "SQL/MM Spatial exception - null argument.";
2032     gaiatopo_set_last_error_msg (accessor, msg);
2033     sqlite3_result_error (context, msg, -1);
2034     return;
2035 
2036   invalid_arg:
2037     msg = "SQL/MM Spatial exception - invalid argument.";
2038     gaiatopo_set_last_error_msg (accessor, msg);
2039     sqlite3_result_error (context, msg, -1);
2040     return;
2041 }
2042 
2043 SPATIALITE_PRIVATE void
fnctaux_NewEdgeHeal(const void * xcontext,int argc,const void * xargv)2044 fnctaux_NewEdgeHeal (const void *xcontext, int argc, const void *xargv)
2045 {
2046 /* SQL function:
2047 / ST_NewEdgeHeal ( text topology-name, int edge_id1, int edge_id2 )
2048 /
2049 / returns: ID of the removed Node
2050 / raises an exception on failure
2051 */
2052     const char *msg;
2053     sqlite3_int64 ret;
2054     const char *topo_name;
2055     sqlite3_int64 edge_id1;
2056     sqlite3_int64 edge_id2;
2057     GaiaTopologyAccessorPtr accessor = NULL;
2058     sqlite3_context *context = (sqlite3_context *) xcontext;
2059     sqlite3_value **argv = (sqlite3_value **) xargv;
2060     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2061     struct splite_internal_cache *cache = sqlite3_user_data (context);
2062     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2063     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2064 	goto null_arg;
2065     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2066 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2067     else
2068 	goto invalid_arg;
2069     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2070 	goto null_arg;
2071     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
2072 	edge_id1 = sqlite3_value_int64 (argv[1]);
2073     else
2074 	goto invalid_arg;
2075     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2076 	goto null_arg;
2077     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2078 	edge_id2 = sqlite3_value_int64 (argv[2]);
2079     else
2080 	goto invalid_arg;
2081 
2082 /* attempting to get a Topology Accessor */
2083     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2084     if (accessor == NULL)
2085 	goto no_topo;
2086     gaiatopo_reset_last_error_msg (accessor);
2087 
2088     start_topo_savepoint (sqlite, cache);
2089     ret = gaiaNewEdgeHeal (accessor, edge_id1, edge_id2);
2090     if (ret < 0)
2091 	rollback_topo_savepoint (sqlite, cache);
2092     else
2093 	release_topo_savepoint (sqlite, cache);
2094     if (ret < 0)
2095       {
2096 	  msg = gaiaGetRtTopoErrorMsg (cache);
2097 	  gaiatopo_set_last_error_msg (accessor, msg);
2098 	  sqlite3_result_error (context, msg, -1);
2099 	  return;
2100       }
2101     sqlite3_result_int64 (context, ret);
2102     return;
2103 
2104   no_topo:
2105     msg = "SQL/MM Spatial exception - invalid topology name.";
2106     gaiatopo_set_last_error_msg (accessor, msg);
2107     sqlite3_result_error (context, msg, -1);
2108     return;
2109 
2110   null_arg:
2111     msg = "SQL/MM Spatial exception - null argument.";
2112     gaiatopo_set_last_error_msg (accessor, msg);
2113     sqlite3_result_error (context, msg, -1);
2114     return;
2115 
2116   invalid_arg:
2117     msg = "SQL/MM Spatial exception - invalid argument.";
2118     gaiatopo_set_last_error_msg (accessor, msg);
2119     sqlite3_result_error (context, msg, -1);
2120     return;
2121 }
2122 
2123 SPATIALITE_PRIVATE void
fnctaux_GetFaceGeometry(const void * xcontext,int argc,const void * xargv)2124 fnctaux_GetFaceGeometry (const void *xcontext, int argc, const void *xargv)
2125 {
2126 /* SQL function:
2127 / ST_GetFaceGeometry ( text topology-name, int face_id )
2128 /
2129 / returns: the Face's geometry (Polygon)
2130 / raises an exception on failure
2131 */
2132     const char *msg;
2133     const char *topo_name;
2134     sqlite3_int64 face_id;
2135     unsigned char *p_blob;
2136     int n_bytes;
2137     gaiaGeomCollPtr geom;
2138     GaiaTopologyAccessorPtr accessor = NULL;
2139     int gpkg_mode = 0;
2140     int tiny_point = 0;
2141     sqlite3_context *context = (sqlite3_context *) xcontext;
2142     sqlite3_value **argv = (sqlite3_value **) xargv;
2143     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2144     struct splite_internal_cache *cache = sqlite3_user_data (context);
2145     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2146     if (cache != NULL)
2147       {
2148 	  gpkg_mode = cache->gpkg_mode;
2149 	  tiny_point = cache->tinyPointEnabled;
2150       }
2151     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2152 	goto null_arg;
2153     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2154 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2155     else
2156 	goto invalid_arg;
2157     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2158 	goto null_arg;
2159     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
2160 	face_id = sqlite3_value_int64 (argv[1]);
2161     else
2162 	goto invalid_arg;
2163 
2164 /* attempting to get a Topology Accessor */
2165     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2166     if (accessor == NULL)
2167 	goto no_topo;
2168     gaiatopo_reset_last_error_msg (accessor);
2169 
2170     geom = gaiaGetFaceGeometry (accessor, face_id);
2171     if (geom == NULL)
2172       {
2173 	  msg = gaiaGetRtTopoErrorMsg (cache);
2174 	  if (msg != NULL)
2175 	    {
2176 		gaiatopo_set_last_error_msg (accessor, msg);
2177 		sqlite3_result_error (context, msg, -1);
2178 		return;
2179 	    }
2180 	  sqlite3_result_null (context);
2181 	  return;
2182       }
2183     gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode, tiny_point);
2184     gaiaFreeGeomColl (geom);
2185     if (p_blob == NULL)
2186 	sqlite3_result_null (context);
2187     else
2188 	sqlite3_result_blob (context, p_blob, n_bytes, free);
2189     return;
2190 
2191   no_topo:
2192     msg = "SQL/MM Spatial exception - invalid topology name.";
2193     gaiatopo_set_last_error_msg (accessor, msg);
2194     sqlite3_result_error (context, msg, -1);
2195     return;
2196 
2197   null_arg:
2198     msg = "SQL/MM Spatial exception - null argument.";
2199     gaiatopo_set_last_error_msg (accessor, msg);
2200     sqlite3_result_error (context, msg, -1);
2201     return;
2202 
2203   invalid_arg:
2204     msg = "SQL/MM Spatial exception - invalid argument.";
2205     gaiatopo_set_last_error_msg (accessor, msg);
2206     sqlite3_result_error (context, msg, -1);
2207     return;
2208 }
2209 
2210 SPATIALITE_PRIVATE void
fnctaux_GetFaceEdges(const void * xcontext,int argc,const void * xargv)2211 fnctaux_GetFaceEdges (const void *xcontext, int argc, const void *xargv)
2212 {
2213 /* SQL function:
2214 / ST_GetFaceEdges ( text topology-name, int face_id )
2215 /
2216 / create/update a table containing an ordered list of EdgeIDs
2217 /
2218 / returns NULL on success
2219 / raises an exception on failure
2220 */
2221     const char *msg;
2222     const char *topo_name;
2223     sqlite3_int64 face_id;
2224     int ret;
2225     GaiaTopologyAccessorPtr accessor = NULL;
2226     sqlite3_context *context = (sqlite3_context *) xcontext;
2227     sqlite3_value **argv = (sqlite3_value **) xargv;
2228     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2229     struct splite_internal_cache *cache = sqlite3_user_data (context);
2230     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2231     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2232 	goto null_arg;
2233     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2234 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2235     else
2236 	goto invalid_arg;
2237     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2238 	goto null_arg;
2239     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
2240 	face_id = sqlite3_value_int64 (argv[1]);
2241     else
2242 	goto invalid_arg;
2243 
2244 /* attempting to get a Topology Accessor */
2245     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2246     if (accessor == NULL)
2247 	goto no_topo;
2248     gaiatopo_reset_last_error_msg (accessor);
2249 
2250     start_topo_savepoint (sqlite, cache);
2251     ret = gaiaGetFaceEdges (accessor, face_id);
2252     if (!ret)
2253 	rollback_topo_savepoint (sqlite, cache);
2254     else
2255 	release_topo_savepoint (sqlite, cache);
2256     if (!ret)
2257       {
2258 	  msg = gaiaGetRtTopoErrorMsg (cache);
2259 	  gaiatopo_set_last_error_msg (accessor, msg);
2260 	  sqlite3_result_error (context, msg, -1);
2261 	  return;
2262       }
2263     sqlite3_result_null (context);
2264     return;
2265 
2266   no_topo:
2267     msg = "SQL/MM Spatial exception - invalid topology name.";
2268     gaiatopo_set_last_error_msg (accessor, msg);
2269     sqlite3_result_error (context, msg, -1);
2270     return;
2271 
2272   null_arg:
2273     msg = "SQL/MM Spatial exception - null argument.";
2274     gaiatopo_set_last_error_msg (accessor, msg);
2275     sqlite3_result_error (context, msg, -1);
2276     return;
2277 
2278   invalid_arg:
2279     msg = "SQL/MM Spatial exception - invalid argument.";
2280     gaiatopo_set_last_error_msg (accessor, msg);
2281     sqlite3_result_error (context, msg, -1);
2282     return;
2283 }
2284 
2285 static int
check_empty_topology(struct gaia_topology * topo)2286 check_empty_topology (struct gaia_topology *topo)
2287 {
2288 /* checking for an empty Topology */
2289     char *sql;
2290     char *table;
2291     char *xtable;
2292     int ret;
2293     int i;
2294     char **results;
2295     int rows;
2296     int columns;
2297     char *errMsg = NULL;
2298     int already_populated = 0;
2299 
2300 /* testing NODE */
2301     table = sqlite3_mprintf ("%s_node", topo->topology_name);
2302     xtable = gaiaDoubleQuotedSql (table);
2303     sqlite3_free (table);
2304     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\"", xtable);
2305     free (xtable);
2306     ret =
2307 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
2308 			   &errMsg);
2309     sqlite3_free (sql);
2310     if (ret != SQLITE_OK)
2311       {
2312 	  sqlite3_free (errMsg);
2313 	  return 0;
2314       }
2315     for (i = 1; i <= rows; i++)
2316       {
2317 	  if (atoi (results[(i * columns) + 0]) > 0)
2318 	      already_populated = 1;
2319       }
2320     sqlite3_free_table (results);
2321     if (already_populated)
2322 	return 0;
2323 
2324 /* testing EDGE */
2325     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
2326     xtable = gaiaDoubleQuotedSql (table);
2327     sqlite3_free (table);
2328     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\"", xtable);
2329     free (xtable);
2330     ret =
2331 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
2332 			   &errMsg);
2333     sqlite3_free (sql);
2334     if (ret != SQLITE_OK)
2335       {
2336 	  sqlite3_free (errMsg);
2337 	  return 0;
2338       }
2339     for (i = 1; i <= rows; i++)
2340       {
2341 	  if (atoi (results[(i * columns) + 0]) > 0)
2342 	      already_populated = 1;
2343       }
2344     sqlite3_free_table (results);
2345     if (already_populated)
2346 	return 0;
2347 
2348 /* testing FACE */
2349     table = sqlite3_mprintf ("%s_face", topo->topology_name);
2350     xtable = gaiaDoubleQuotedSql (table);
2351     sqlite3_free (table);
2352     sql =
2353 	sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\" WHERE face_id <> 0",
2354 			 xtable);
2355     free (xtable);
2356     ret =
2357 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
2358 			   &errMsg);
2359     sqlite3_free (sql);
2360     if (ret != SQLITE_OK)
2361       {
2362 	  sqlite3_free (errMsg);
2363 	  return 0;
2364       }
2365     for (i = 1; i <= rows; i++)
2366       {
2367 	  if (atoi (results[(i * columns) + 0]) > 0)
2368 	      already_populated = 1;
2369       }
2370     sqlite3_free_table (results);
2371     if (already_populated)
2372 	return 0;
2373 
2374     return 1;
2375 }
2376 
2377 SPATIALITE_PRIVATE void
fnctaux_ValidateTopoGeo(const void * xcontext,int argc,const void * xargv)2378 fnctaux_ValidateTopoGeo (const void *xcontext, int argc, const void *xargv)
2379 {
2380 /* SQL function:
2381 / ST_ValidateTopoGeo ( text topology-name )
2382 /
2383 / create/update a table containing an validation report for a given TopoGeo
2384 /
2385 / returns NULL on success
2386 / raises an exception on failure
2387 */
2388     const char *msg;
2389     const char *topo_name;
2390     int ret;
2391     GaiaTopologyAccessorPtr accessor = NULL;
2392     struct gaia_topology *topo;
2393     sqlite3_context *context = (sqlite3_context *) xcontext;
2394     sqlite3_value **argv = (sqlite3_value **) xargv;
2395     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2396     struct splite_internal_cache *cache = sqlite3_user_data (context);
2397     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2398     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2399 	goto null_arg;
2400     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2401 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2402     else
2403 	goto invalid_arg;
2404 
2405 /* attempting to get a Topology Accessor */
2406     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2407     if (accessor == NULL)
2408 	goto no_topo;
2409     gaiatopo_reset_last_error_msg (accessor);
2410     topo = (struct gaia_topology *) accessor;
2411     if (check_empty_topology (topo))
2412 	goto empty;
2413 
2414     start_topo_savepoint (sqlite, cache);
2415     ret = gaiaValidateTopoGeo (accessor);
2416     if (!ret)
2417 	rollback_topo_savepoint (sqlite, cache);
2418     else
2419 	release_topo_savepoint (sqlite, cache);
2420     if (!ret)
2421       {
2422 	  msg = gaiaGetRtTopoErrorMsg (cache);
2423 	  gaiatopo_set_last_error_msg (accessor, msg);
2424 	  sqlite3_result_error (context, msg, -1);
2425 	  return;
2426       }
2427     sqlite3_result_null (context);
2428     return;
2429 
2430   no_topo:
2431     msg = "SQL/MM Spatial exception - invalid topology name.";
2432     gaiatopo_set_last_error_msg (accessor, msg);
2433     sqlite3_result_error (context, msg, -1);
2434     return;
2435 
2436   null_arg:
2437     msg = "SQL/MM Spatial exception - null argument.";
2438     gaiatopo_set_last_error_msg (accessor, msg);
2439     sqlite3_result_error (context, msg, -1);
2440     return;
2441 
2442   invalid_arg:
2443     msg = "SQL/MM Spatial exception - invalid argument.";
2444     gaiatopo_set_last_error_msg (accessor, msg);
2445     sqlite3_result_error (context, msg, -1);
2446     return;
2447 
2448   empty:
2449     msg = "SQL/MM Spatial exception - empty topology.";
2450     gaiatopo_set_last_error_msg (accessor, msg);
2451     sqlite3_result_error (context, msg, -1);
2452     return;
2453 }
2454 
2455 SPATIALITE_PRIVATE void
fnctaux_CreateTopoGeo(const void * xcontext,int argc,const void * xargv)2456 fnctaux_CreateTopoGeo (const void *xcontext, int argc, const void *xargv)
2457 {
2458 /* SQL function:
2459 / ST_CreateTopoGeo ( text topology-name , blob geom-collection )
2460 /
2461 / creates and populates an empty Topology by importing a Geometry-collection
2462 /
2463 / returns NULL on success
2464 / raises an exception on failure
2465 */
2466     const char *msg;
2467     const char *topo_name;
2468     int ret;
2469     const unsigned char *blob;
2470     int blob_sz;
2471     gaiaGeomCollPtr geom = NULL;
2472     int gpkg_amphibious = 0;
2473     int gpkg_mode = 0;
2474     GaiaTopologyAccessorPtr accessor = NULL;
2475     struct gaia_topology *topo;
2476     sqlite3_context *context = (sqlite3_context *) xcontext;
2477     sqlite3_value **argv = (sqlite3_value **) xargv;
2478     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2479     struct splite_internal_cache *cache = sqlite3_user_data (context);
2480     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2481     if (cache != NULL)
2482       {
2483 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2484 	  gpkg_mode = cache->gpkg_mode;
2485       }
2486     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2487 	goto null_arg;
2488     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2489 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2490     else
2491 	goto invalid_arg;
2492     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2493 	goto null_arg;
2494     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2495       {
2496 	  blob = sqlite3_value_blob (argv[1]);
2497 	  blob_sz = sqlite3_value_bytes (argv[1]);
2498 	  geom =
2499 	      gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
2500 					   gpkg_amphibious);
2501       }
2502     else
2503 	goto invalid_arg;
2504     if (geom == NULL)
2505 	goto not_geom;
2506 
2507 /* attempting to get a Topology Accessor */
2508     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2509     if (accessor == NULL)
2510 	goto no_topo;
2511     gaiatopo_reset_last_error_msg (accessor);
2512     topo = (struct gaia_topology *) accessor;
2513     if (!check_empty_topology (topo))
2514 	goto not_empty;
2515     if (!check_matching_srid_dims (accessor, geom->Srid, geom->DimensionModel))
2516 	goto invalid_geom;
2517 
2518     start_topo_savepoint (sqlite, cache);
2519     ret =
2520 	auxtopo_insert_into_topology (accessor, geom, 0.0, -1, -1,
2521 				      GAIA_MODE_TOPO_FACE, NULL);
2522     if (!ret)
2523 	rollback_topo_savepoint (sqlite, cache);
2524     else
2525 	release_topo_savepoint (sqlite, cache);
2526     if (!ret)
2527       {
2528 	  msg = gaiaGetRtTopoErrorMsg (cache);
2529 	  gaiatopo_set_last_error_msg (accessor, msg);
2530 	  sqlite3_result_error (context, msg, -1);
2531 	  return;
2532       }
2533     sqlite3_result_null (context);
2534     gaiaFreeGeomColl (geom);
2535     return;
2536 
2537   no_topo:
2538     if (geom != NULL)
2539 	gaiaFreeGeomColl (geom);
2540     msg = "SQL/MM Spatial exception - invalid topology name.";
2541     gaiatopo_set_last_error_msg (accessor, msg);
2542     sqlite3_result_error (context, msg, -1);
2543     return;
2544 
2545   null_arg:
2546     if (geom != NULL)
2547 	gaiaFreeGeomColl (geom);
2548     msg = "SQL/MM Spatial exception - null argument.";
2549     gaiatopo_set_last_error_msg (accessor, msg);
2550     sqlite3_result_error (context, msg, -1);
2551     return;
2552 
2553   invalid_arg:
2554     if (geom != NULL)
2555 	gaiaFreeGeomColl (geom);
2556     msg = "SQL/MM Spatial exception - invalid argument.";
2557     gaiatopo_set_last_error_msg (accessor, msg);
2558     sqlite3_result_error (context, msg, -1);
2559     return;
2560 
2561   not_empty:
2562     if (geom != NULL)
2563 	gaiaFreeGeomColl (geom);
2564     msg = "SQL/MM Spatial exception - non-empty topology.";
2565     gaiatopo_set_last_error_msg (accessor, msg);
2566     sqlite3_result_error (context, msg, -1);
2567     return;
2568 
2569   not_geom:
2570     if (geom != NULL)
2571 	gaiaFreeGeomColl (geom);
2572     msg = "SQL/MM Spatial exception - not a Geometry.";
2573     gaiatopo_set_last_error_msg (accessor, msg);
2574     sqlite3_result_error (context, msg, -1);
2575     return;
2576 
2577   invalid_geom:
2578     if (geom != NULL)
2579 	gaiaFreeGeomColl (geom);
2580     msg =
2581 	"SQL/MM Spatial exception - invalid Geometry (mismatching SRID or dimensions).";
2582     gaiatopo_set_last_error_msg (accessor, msg);
2583     sqlite3_result_error (context, msg, -1);
2584     return;
2585 }
2586 
2587 SPATIALITE_PRIVATE void
fnctaux_GetNodeByPoint(const void * xcontext,int argc,const void * xargv)2588 fnctaux_GetNodeByPoint (const void *xcontext, int argc, const void *xargv)
2589 {
2590 /* SQL function:
2591 / GetNodeByPoint ( text topology-name, Geometry point )
2592 / GetNodeByPoint ( text topology-name, Geometry point, double tolerance )
2593 /
2594 / returns: the ID of some Node on success, 0 if no Node was found
2595 / raises an exception on failure
2596 */
2597     const char *msg;
2598     sqlite3_int64 ret;
2599     const char *topo_name;
2600     unsigned char *p_blob;
2601     int n_bytes;
2602     gaiaGeomCollPtr point = NULL;
2603     gaiaPointPtr pt;
2604     double tolerance = -1;
2605     int invalid = 0;
2606     GaiaTopologyAccessorPtr accessor = NULL;
2607     int gpkg_amphibious = 0;
2608     int gpkg_mode = 0;
2609     sqlite3_context *context = (sqlite3_context *) xcontext;
2610     sqlite3_value **argv = (sqlite3_value **) xargv;
2611     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2612     struct splite_internal_cache *cache = sqlite3_user_data (context);
2613     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2614     if (cache != NULL)
2615       {
2616 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2617 	  gpkg_mode = cache->gpkg_mode;
2618       }
2619     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2620 	goto null_arg;
2621     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2622 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2623     else
2624 	goto invalid_arg;
2625     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2626 	goto null_arg;
2627     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2628       {
2629 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
2630 	  n_bytes = sqlite3_value_bytes (argv[1]);
2631       }
2632     else
2633 	goto invalid_arg;
2634     if (argc >= 3)
2635       {
2636 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2637 	      goto null_arg;
2638 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2639 	    {
2640 		int t = sqlite3_value_int (argv[2]);
2641 		tolerance = t;
2642 	    }
2643 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
2644 	      tolerance = sqlite3_value_double (argv[2]);
2645 	  else
2646 	      goto invalid_arg;
2647 	  if (tolerance < 0.0)
2648 	      goto negative_tolerance;
2649       }
2650 
2651 /* attempting to get a Point Geometry */
2652     point =
2653 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
2654 				     gpkg_amphibious);
2655     if (!point)
2656 	goto invalid_arg;
2657     if (point->FirstLinestring != NULL)
2658 	invalid = 1;
2659     if (point->FirstPolygon != NULL)
2660 	invalid = 1;
2661     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
2662 	invalid = 1;
2663     if (invalid)
2664 	goto invalid_arg;
2665 
2666 /* attempting to get a Topology Accessor */
2667     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2668     if (accessor == NULL)
2669 	goto no_topo;
2670 
2671     gaiatopo_reset_last_error_msg (accessor);
2672     pt = point->FirstPoint;
2673 
2674     ret = gaiaGetNodeByPoint (accessor, pt, tolerance);
2675     gaiaFreeGeomColl (point);
2676     point = NULL;
2677     if (ret < 0)
2678       {
2679 	  msg = gaiaGetRtTopoErrorMsg (cache);
2680 	  gaiatopo_set_last_error_msg (accessor, msg);
2681 	  sqlite3_result_error (context, msg, -1);
2682 	  return;
2683       }
2684     sqlite3_result_int64 (context, ret);
2685     return;
2686 
2687   no_topo:
2688     if (point != NULL)
2689 	gaiaFreeGeomColl (point);
2690     msg = "SQL/MM Spatial exception - invalid topology name.";
2691     gaiatopo_set_last_error_msg (accessor, msg);
2692     sqlite3_result_error (context, msg, -1);
2693     return;
2694 
2695   null_arg:
2696     if (point != NULL)
2697 	gaiaFreeGeomColl (point);
2698     msg = "SQL/MM Spatial exception - null argument.";
2699     gaiatopo_set_last_error_msg (accessor, msg);
2700     sqlite3_result_error (context, msg, -1);
2701     return;
2702 
2703   invalid_arg:
2704     if (point != NULL)
2705 	gaiaFreeGeomColl (point);
2706     msg = "SQL/MM Spatial exception - invalid argument.";
2707     gaiatopo_set_last_error_msg (accessor, msg);
2708     sqlite3_result_error (context, msg, -1);
2709     return;
2710 
2711   negative_tolerance:
2712     if (point != NULL)
2713 	gaiaFreeGeomColl (point);
2714     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
2715     gaiatopo_set_last_error_msg (accessor, msg);
2716     sqlite3_result_error (context, msg, -1);
2717     return;
2718 }
2719 
2720 SPATIALITE_PRIVATE void
fnctaux_GetEdgeByPoint(const void * xcontext,int argc,const void * xargv)2721 fnctaux_GetEdgeByPoint (const void *xcontext, int argc, const void *xargv)
2722 {
2723 /* SQL function:
2724 / GetEdgeByPoint ( text topology-name, Geometry point )
2725 / GetEdgeByPoint ( text topology-name, Geometry point, double tolerance )
2726 /
2727 / returns: the ID of some Edge on success
2728 / raises an exception on failure
2729 */
2730     const char *msg;
2731     sqlite3_int64 ret;
2732     const char *topo_name;
2733     unsigned char *p_blob;
2734     int n_bytes;
2735     gaiaGeomCollPtr point = NULL;
2736     gaiaPointPtr pt;
2737     double tolerance = -1;
2738     int invalid = 0;
2739     GaiaTopologyAccessorPtr accessor = NULL;
2740     int gpkg_amphibious = 0;
2741     int gpkg_mode = 0;
2742     sqlite3_context *context = (sqlite3_context *) xcontext;
2743     sqlite3_value **argv = (sqlite3_value **) xargv;
2744     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2745     struct splite_internal_cache *cache = sqlite3_user_data (context);
2746     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2747     if (cache != NULL)
2748       {
2749 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2750 	  gpkg_mode = cache->gpkg_mode;
2751       }
2752     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2753 	goto null_arg;
2754     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2755 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2756     else
2757 	goto invalid_arg;
2758     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2759 	goto null_arg;
2760     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2761       {
2762 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
2763 	  n_bytes = sqlite3_value_bytes (argv[1]);
2764       }
2765     else
2766 	goto invalid_arg;
2767     if (argc >= 3)
2768       {
2769 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2770 	      goto null_arg;
2771 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2772 	    {
2773 		int t = sqlite3_value_int (argv[2]);
2774 		tolerance = t;
2775 	    }
2776 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
2777 	      tolerance = sqlite3_value_double (argv[2]);
2778 	  else
2779 	      goto invalid_arg;
2780 	  if (tolerance < 0.0)
2781 	      goto negative_tolerance;
2782       }
2783 
2784 /* attempting to get a Point Geometry */
2785     point =
2786 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
2787 				     gpkg_amphibious);
2788     if (!point)
2789 	goto invalid_arg;
2790     if (point->FirstLinestring != NULL)
2791 	invalid = 1;
2792     if (point->FirstPolygon != NULL)
2793 	invalid = 1;
2794     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
2795 	invalid = 1;
2796     if (invalid)
2797 	goto invalid_arg;
2798 
2799 /* attempting to get a Topology Accessor */
2800     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2801     if (accessor == NULL)
2802 	goto no_topo;
2803     gaiatopo_reset_last_error_msg (accessor);
2804     pt = point->FirstPoint;
2805 
2806     ret = gaiaGetEdgeByPoint (accessor, pt, tolerance);
2807     gaiaFreeGeomColl (point);
2808     point = NULL;
2809     if (ret < 0)
2810       {
2811 	  msg = gaiaGetRtTopoErrorMsg (cache);
2812 	  gaiatopo_set_last_error_msg (accessor, msg);
2813 	  sqlite3_result_error (context, msg, -1);
2814 	  return;
2815       }
2816     sqlite3_result_int64 (context, ret);
2817     return;
2818 
2819   no_topo:
2820     if (point != NULL)
2821 	gaiaFreeGeomColl (point);
2822     msg = "SQL/MM Spatial exception - invalid topology name.";
2823     gaiatopo_set_last_error_msg (accessor, msg);
2824     sqlite3_result_error (context, msg, -1);
2825     return;
2826 
2827   null_arg:
2828     if (point != NULL)
2829 	gaiaFreeGeomColl (point);
2830     msg = "SQL/MM Spatial exception - null argument.";
2831     gaiatopo_set_last_error_msg (accessor, msg);
2832     sqlite3_result_error (context, msg, -1);
2833     return;
2834 
2835   invalid_arg:
2836     if (point != NULL)
2837 	gaiaFreeGeomColl (point);
2838     msg = "SQL/MM Spatial exception - invalid argument.";
2839     gaiatopo_set_last_error_msg (accessor, msg);
2840     sqlite3_result_error (context, msg, -1);
2841     return;
2842 
2843   negative_tolerance:
2844     if (point != NULL)
2845 	gaiaFreeGeomColl (point);
2846     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
2847     gaiatopo_set_last_error_msg (accessor, msg);
2848     sqlite3_result_error (context, msg, -1);
2849     return;
2850 }
2851 
2852 SPATIALITE_PRIVATE void
fnctaux_GetFaceByPoint(const void * xcontext,int argc,const void * xargv)2853 fnctaux_GetFaceByPoint (const void *xcontext, int argc, const void *xargv)
2854 {
2855 /* SQL function:
2856 / GetFaceByPoint ( text topology-name, Geometry point )
2857 / GetFaceByPoint ( text topology-name, Geometry point, double tolerance )
2858 /
2859 / returns: the ID of some Face on success
2860 / raises an exception on failure
2861 */
2862     const char *msg;
2863     sqlite3_int64 ret;
2864     const char *topo_name;
2865     unsigned char *p_blob;
2866     int n_bytes;
2867     gaiaGeomCollPtr point = NULL;
2868     gaiaPointPtr pt;
2869     double tolerance = -1;
2870     int invalid = 0;
2871     GaiaTopologyAccessorPtr accessor = NULL;
2872     int gpkg_amphibious = 0;
2873     int gpkg_mode = 0;
2874     sqlite3_context *context = (sqlite3_context *) xcontext;
2875     sqlite3_value **argv = (sqlite3_value **) xargv;
2876     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2877     struct splite_internal_cache *cache = sqlite3_user_data (context);
2878     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2879     if (cache != NULL)
2880       {
2881 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2882 	  gpkg_mode = cache->gpkg_mode;
2883       }
2884     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2885 	goto null_arg;
2886     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2887 	topo_name = (const char *) sqlite3_value_text (argv[0]);
2888     else
2889 	goto invalid_arg;
2890     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2891 	goto null_arg;
2892     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2893       {
2894 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
2895 	  n_bytes = sqlite3_value_bytes (argv[1]);
2896       }
2897     else
2898 	goto invalid_arg;
2899     if (argc >= 3)
2900       {
2901 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2902 	      goto null_arg;
2903 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2904 	    {
2905 		int t = sqlite3_value_int (argv[2]);
2906 		tolerance = t;
2907 	    }
2908 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
2909 	      tolerance = sqlite3_value_double (argv[2]);
2910 	  else
2911 	      goto invalid_arg;
2912 	  if (tolerance < 0.0)
2913 	      goto negative_tolerance;
2914       }
2915 
2916 /* attempting to get a Point Geometry */
2917     point =
2918 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
2919 				     gpkg_amphibious);
2920     if (!point)
2921 	goto invalid_arg;
2922     if (point->FirstLinestring != NULL)
2923 	invalid = 1;
2924     if (point->FirstPolygon != NULL)
2925 	invalid = 1;
2926     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
2927 	invalid = 1;
2928     if (invalid)
2929 	goto invalid_arg;
2930 
2931 /* attempting to get a Topology Accessor */
2932     accessor = gaiaGetTopology (sqlite, cache, topo_name);
2933     if (accessor == NULL)
2934 	goto no_topo;
2935     gaiatopo_reset_last_error_msg (accessor);
2936     pt = point->FirstPoint;
2937 
2938     ret = gaiaGetFaceByPoint (accessor, pt, tolerance);
2939     gaiaFreeGeomColl (point);
2940     point = NULL;
2941     if (ret < 0)
2942       {
2943 	  msg = gaiaGetRtTopoErrorMsg (cache);
2944 	  gaiatopo_set_last_error_msg (accessor, msg);
2945 	  sqlite3_result_error (context, msg, -1);
2946 	  return;
2947       }
2948     sqlite3_result_int64 (context, ret);
2949     return;
2950 
2951   no_topo:
2952     if (point != NULL)
2953 	gaiaFreeGeomColl (point);
2954     msg = "SQL/MM Spatial exception - invalid topology name.";
2955     gaiatopo_set_last_error_msg (accessor, msg);
2956     sqlite3_result_error (context, msg, -1);
2957     return;
2958 
2959   null_arg:
2960     if (point != NULL)
2961 	gaiaFreeGeomColl (point);
2962     msg = "SQL/MM Spatial exception - null argument.";
2963     gaiatopo_set_last_error_msg (accessor, msg);
2964     sqlite3_result_error (context, msg, -1);
2965     return;
2966 
2967   invalid_arg:
2968     if (point != NULL)
2969 	gaiaFreeGeomColl (point);
2970     msg = "SQL/MM Spatial exception - invalid argument.";
2971     gaiatopo_set_last_error_msg (accessor, msg);
2972     sqlite3_result_error (context, msg, -1);
2973     return;
2974 
2975   negative_tolerance:
2976     if (point != NULL)
2977 	gaiaFreeGeomColl (point);
2978     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
2979     gaiatopo_set_last_error_msg (accessor, msg);
2980     sqlite3_result_error (context, msg, -1);
2981     return;
2982 }
2983 
2984 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_AddPoint(const void * xcontext,int argc,const void * xargv)2985 fnctaux_TopoGeo_AddPoint (const void *xcontext, int argc, const void *xargv)
2986 {
2987 /* SQL function:
2988 / TopoGeo_AddPoint ( text topology-name, Geometry (multi)point )
2989 / TopoGeo_AddPoint ( text topology-name, Geometry (multi)point, double tolerance )
2990 /
2991 / returns: a comma separated list of all IDs of corresponding Nodes on success
2992 / raises an exception on failure
2993 */
2994     const char *msg;
2995     sqlite3_int64 node_id;
2996     char xnode_id[64];
2997     char *retlist = NULL;
2998     char *savelist;
2999     const char *topo_name;
3000     unsigned char *p_blob;
3001     int n_bytes;
3002     gaiaGeomCollPtr point = NULL;
3003     gaiaPointPtr pt;
3004     double tolerance = -1;
3005     int invalid = 0;
3006     GaiaTopologyAccessorPtr accessor = NULL;
3007     int gpkg_amphibious = 0;
3008     int gpkg_mode = 0;
3009     sqlite3_context *context = (sqlite3_context *) xcontext;
3010     sqlite3_value **argv = (sqlite3_value **) xargv;
3011     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3012     struct splite_internal_cache *cache = sqlite3_user_data (context);
3013     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3014     if (cache != NULL)
3015       {
3016 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
3017 	  gpkg_mode = cache->gpkg_mode;
3018       }
3019     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3020 	goto null_arg;
3021     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3022 	topo_name = (const char *) sqlite3_value_text (argv[0]);
3023     else
3024 	goto invalid_arg;
3025     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3026 	goto null_arg;
3027     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
3028       {
3029 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
3030 	  n_bytes = sqlite3_value_bytes (argv[1]);
3031       }
3032     else
3033 	goto invalid_arg;
3034     if (argc >= 3)
3035       {
3036 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3037 	      goto null_arg;
3038 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
3039 	    {
3040 		int t = sqlite3_value_int (argv[2]);
3041 		tolerance = t;
3042 	    }
3043 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
3044 	      tolerance = sqlite3_value_double (argv[2]);
3045 	  else
3046 	      goto invalid_arg;
3047 	  if (tolerance < 0.0)
3048 	      goto negative_tolerance;
3049       }
3050 
3051 /* attempting to get a Point Geometry */
3052     point =
3053 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
3054 				     gpkg_amphibious);
3055     if (!point)
3056 	goto invalid_arg;
3057     if (point->FirstLinestring != NULL)
3058 	invalid = 1;
3059     if (point->FirstPolygon != NULL)
3060 	invalid = 1;
3061     if (point->FirstPoint == NULL)
3062 	invalid = 1;
3063     if (invalid)
3064 	goto invalid_arg;
3065 
3066 /* attempting to get a Topology Accessor */
3067     accessor = gaiaGetTopology (sqlite, cache, topo_name);
3068     if (accessor == NULL)
3069 	goto no_topo;
3070     gaiatopo_reset_last_error_msg (accessor);
3071     if (!check_matching_srid_dims
3072 	(accessor, point->Srid, point->DimensionModel))
3073 	goto invalid_geom;
3074 
3075     start_topo_savepoint (sqlite, cache);
3076     pt = point->FirstPoint;
3077     while (pt != NULL)
3078       {
3079 	  /* looping on elementary Points */
3080 	  node_id = gaiaTopoGeo_AddPoint (accessor, pt, tolerance);
3081 	  if (node_id < 0)
3082 	      break;
3083 	  sprintf (xnode_id, "%lld", node_id);
3084 	  if (retlist == NULL)
3085 	      retlist = sqlite3_mprintf ("%s", xnode_id);
3086 	  else
3087 	    {
3088 		savelist = retlist;
3089 		retlist = sqlite3_mprintf ("%s, %s", savelist, xnode_id);
3090 		sqlite3_free (savelist);
3091 	    }
3092 	  pt = pt->Next;
3093       }
3094 
3095     if (node_id < 0)
3096 	rollback_topo_savepoint (sqlite, cache);
3097     else
3098 	release_topo_savepoint (sqlite, cache);
3099     gaiaFreeGeomColl (point);
3100     point = NULL;
3101     if (node_id < 0)
3102       {
3103 	  msg = gaiaGetRtTopoErrorMsg (cache);
3104 	  gaiatopo_set_last_error_msg (accessor, msg);
3105 	  sqlite3_result_error (context, msg, -1);
3106 	  if (retlist != NULL)
3107 	      sqlite3_free (retlist);
3108 	  return;
3109       }
3110     sqlite3_result_text (context, retlist, strlen (retlist), sqlite3_free);
3111     return;
3112 
3113   no_topo:
3114     if (point != NULL)
3115 	gaiaFreeGeomColl (point);
3116     msg = "SQL/MM Spatial exception - invalid topology name.";
3117     gaiatopo_set_last_error_msg (accessor, msg);
3118     sqlite3_result_error (context, msg, -1);
3119     return;
3120 
3121   null_arg:
3122     if (point != NULL)
3123 	gaiaFreeGeomColl (point);
3124     msg = "SQL/MM Spatial exception - null argument.";
3125     gaiatopo_set_last_error_msg (accessor, msg);
3126     sqlite3_result_error (context, msg, -1);
3127     return;
3128 
3129   invalid_arg:
3130     if (point != NULL)
3131 	gaiaFreeGeomColl (point);
3132     msg = "SQL/MM Spatial exception - invalid argument.";
3133     gaiatopo_set_last_error_msg (accessor, msg);
3134     sqlite3_result_error (context, msg, -1);
3135     return;
3136 
3137   invalid_geom:
3138     if (point != NULL)
3139 	gaiaFreeGeomColl (point);
3140     msg =
3141 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
3142     gaiatopo_set_last_error_msg (accessor, msg);
3143     sqlite3_result_error (context, msg, -1);
3144     return;
3145 
3146   negative_tolerance:
3147     if (point != NULL)
3148 	gaiaFreeGeomColl (point);
3149     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
3150     gaiatopo_set_last_error_msg (accessor, msg);
3151     sqlite3_result_error (context, msg, -1);
3152     return;
3153 }
3154 
3155 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_AddLineString(const void * xcontext,int argc,const void * xargv)3156 fnctaux_TopoGeo_AddLineString (const void *xcontext, int argc,
3157 			       const void *xargv)
3158 {
3159 /* SQL function:
3160 / TopoGeo_AddLineString ( text topology-name, Geometry (multi)linestring )
3161 / TopoGeo_AddLineString ( text topology-name, Geometry (multi)linestring, double tolerance )
3162 /
3163 / returns: a comma separated list of all IDs of corresponding Edges on success
3164 / raises an exception on failure
3165 */
3166     const char *msg;
3167     int ret;
3168     char xedge_id[64];
3169     sqlite3_int64 *edge_ids = NULL;
3170     int ids_count = 0;
3171     char *retlist = NULL;
3172     char *savelist;
3173     int i;
3174     const char *topo_name;
3175     unsigned char *p_blob;
3176     int n_bytes;
3177     gaiaGeomCollPtr linestring = NULL;
3178     gaiaLinestringPtr ln;
3179     double tolerance = -1;
3180     int invalid = 0;
3181     GaiaTopologyAccessorPtr accessor = NULL;
3182     int gpkg_amphibious = 0;
3183     int gpkg_mode = 0;
3184     sqlite3_context *context = (sqlite3_context *) xcontext;
3185     sqlite3_value **argv = (sqlite3_value **) xargv;
3186     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3187     struct splite_internal_cache *cache = sqlite3_user_data (context);
3188     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3189     if (cache != NULL)
3190       {
3191 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
3192 	  gpkg_mode = cache->gpkg_mode;
3193       }
3194     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3195 	goto null_arg;
3196     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3197 	topo_name = (const char *) sqlite3_value_text (argv[0]);
3198     else
3199 	goto invalid_arg;
3200     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3201 	goto null_arg;
3202     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
3203       {
3204 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
3205 	  n_bytes = sqlite3_value_bytes (argv[1]);
3206       }
3207     else
3208 	goto invalid_arg;
3209     if (argc >= 3)
3210       {
3211 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3212 	      goto null_arg;
3213 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
3214 	    {
3215 		int t = sqlite3_value_int (argv[2]);
3216 		tolerance = t;
3217 	    }
3218 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
3219 	      tolerance = sqlite3_value_double (argv[2]);
3220 	  else
3221 	      goto invalid_arg;
3222 	  if (tolerance < 0.0)
3223 	      goto negative_tolerance;
3224       }
3225 
3226 /* attempting to get a Linestring Geometry */
3227     linestring =
3228 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
3229 				     gpkg_amphibious);
3230     if (!linestring)
3231 	goto invalid_arg;
3232     if (linestring->FirstPoint != NULL)
3233 	invalid = 1;
3234     if (linestring->FirstPolygon != NULL)
3235 	invalid = 1;
3236     if (linestring->FirstLinestring == NULL)
3237 	invalid = 1;
3238     if (invalid)
3239 	goto invalid_arg;
3240 
3241 /* attempting to get a Topology Accessor */
3242     accessor = gaiaGetTopology (sqlite, cache, topo_name);
3243     if (accessor == NULL)
3244 	goto no_topo;
3245     gaiatopo_reset_last_error_msg (accessor);
3246     if (!check_matching_srid_dims
3247 	(accessor, linestring->Srid, linestring->DimensionModel))
3248 	goto invalid_geom;
3249 
3250     start_topo_savepoint (sqlite, cache);
3251     ln = linestring->FirstLinestring;
3252     while (ln != NULL)
3253       {
3254 	  /* looping on individual Linestrings */
3255 	  ret =
3256 	      gaiaTopoGeo_AddLineString (accessor, ln, tolerance, &edge_ids,
3257 					 &ids_count);
3258 	  if (!ret)
3259 	      break;
3260 	  for (i = 0; i < ids_count; i++)
3261 	    {
3262 		sprintf (xedge_id, "%lld", edge_ids[i]);
3263 		if (retlist == NULL)
3264 		    retlist = sqlite3_mprintf ("%s", xedge_id);
3265 		else
3266 		  {
3267 		      savelist = retlist;
3268 		      retlist = sqlite3_mprintf ("%s, %s", savelist, xedge_id);
3269 		      sqlite3_free (savelist);
3270 		  }
3271 	    }
3272 	  free (edge_ids);
3273 	  ln = ln->Next;
3274       }
3275 
3276     if (!ret)
3277 	rollback_topo_savepoint (sqlite, cache);
3278     else
3279 	release_topo_savepoint (sqlite, cache);
3280     gaiaFreeGeomColl (linestring);
3281     linestring = NULL;
3282     if (!ret)
3283       {
3284 	  msg = gaiaGetRtTopoErrorMsg (cache);
3285 	  gaiatopo_set_last_error_msg (accessor, msg);
3286 	  sqlite3_result_error (context, msg, -1);
3287 	  sqlite3_free (retlist);
3288 	  return;
3289       }
3290     sqlite3_result_text (context, retlist, strlen (retlist), sqlite3_free);
3291     return;
3292 
3293   no_topo:
3294     if (linestring != NULL)
3295 	gaiaFreeGeomColl (linestring);
3296     msg = "SQL/MM Spatial exception - invalid topology name.";
3297     gaiatopo_set_last_error_msg (accessor, msg);
3298     sqlite3_result_error (context, msg, -1);
3299     return;
3300 
3301   null_arg:
3302     if (linestring != NULL)
3303 	gaiaFreeGeomColl (linestring);
3304     msg = "SQL/MM Spatial exception - null argument.";
3305     gaiatopo_set_last_error_msg (accessor, msg);
3306     sqlite3_result_error (context, msg, -1);
3307     return;
3308 
3309   invalid_arg:
3310     if (linestring != NULL)
3311 	gaiaFreeGeomColl (linestring);
3312     msg = "SQL/MM Spatial exception - invalid argument.";
3313     gaiatopo_set_last_error_msg (accessor, msg);
3314     sqlite3_result_error (context, msg, -1);
3315     return;
3316 
3317   invalid_geom:
3318     if (linestring != NULL)
3319 	gaiaFreeGeomColl (linestring);
3320     msg =
3321 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
3322     gaiatopo_set_last_error_msg (accessor, msg);
3323     sqlite3_result_error (context, msg, -1);
3324     return;
3325 
3326   negative_tolerance:
3327     if (linestring != NULL)
3328 	gaiaFreeGeomColl (linestring);
3329     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
3330     gaiatopo_set_last_error_msg (accessor, msg);
3331     sqlite3_result_error (context, msg, -1);
3332     return;
3333 }
3334 
3335 static int
kill_all_existing_faces(sqlite3 * sqlite,char * toponame)3336 kill_all_existing_faces (sqlite3 * sqlite, char *toponame)
3337 {
3338 /* to be executed before invoking any NO FACE function */
3339     char *sql;
3340     char *table;
3341     char *xtable;
3342     int ret;
3343     char *errMsg = NULL;
3344 
3345 /* invalidating all relationships between Edges and Faces */
3346     table = sqlite3_mprintf ("%s_edge", toponame);
3347     xtable = gaiaDoubleQuotedSql (table);
3348     sqlite3_free (table);
3349     sql =
3350 	sqlite3_mprintf
3351 	("UPDATE \"%s\" SET left_face = NULL, right_face = NULL "
3352 	 "WHERE left_face IS NOT NULL OR right_face IS NOT NULL", xtable);
3353     free (xtable);
3354     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
3355     sqlite3_free (sql);
3356     if (ret != SQLITE_OK)
3357       {
3358 	  spatialite_e ("NoFace invalidate Edge/Face: %s\n", errMsg);
3359 	  sqlite3_free (errMsg);
3360 	  return 0;
3361       }
3362 
3363 /* invalidating all relationships between Nodes and Faces */
3364     table = sqlite3_mprintf ("%s_node", toponame);
3365     xtable = gaiaDoubleQuotedSql (table);
3366     sqlite3_free (table);
3367     sql =
3368 	sqlite3_mprintf
3369 	("UPDATE \"%s\" SET containing_face = NULL "
3370 	 "WHERE containing_face IS NOT NULL", xtable);
3371     free (xtable);
3372     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
3373     sqlite3_free (sql);
3374     if (ret != SQLITE_OK)
3375       {
3376 	  spatialite_e ("NoFace invalidate Node/Face: %s\n", errMsg);
3377 	  sqlite3_free (errMsg);
3378 	  return 0;
3379       }
3380 
3381 /* removing all Faces except the Universe */
3382     table = sqlite3_mprintf ("%s_face", toponame);
3383     xtable = gaiaDoubleQuotedSql (table);
3384     sqlite3_free (table);
3385     sql = sqlite3_mprintf ("DELETE FROM \"%s\" WHERE face_id <> 0", xtable);
3386     free (xtable);
3387     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &errMsg);
3388     sqlite3_free (sql);
3389     if (ret != SQLITE_OK)
3390       {
3391 	  spatialite_e ("cazzo NoFace remove Faces: %s\n", errMsg);
3392 	  sqlite3_free (errMsg);
3393 	  return 0;
3394       }
3395     return 1;
3396 }
3397 
3398 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_AddLineStringNoFace(const void * xcontext,int argc,const void * xargv)3399 fnctaux_TopoGeo_AddLineStringNoFace (const void *xcontext, int argc,
3400 				     const void *xargv)
3401 {
3402 /* SQL function:
3403 / TopoGeo_AddLineStringNoFace ( text topology-name, Geometry (multi)linestring )
3404 / TopoGeo_AddLineStringNoFace ( text topology-name, Geometry (multi)linestring,
3405 /                               double tolerance )
3406 /
3407 / returns: a comma separated list of all IDs of corresponding Edges on success
3408 / raises an exception on failure
3409 */
3410     const char *msg;
3411     int ret;
3412     char xedge_id[64];
3413     sqlite3_int64 *edge_ids = NULL;
3414     int ids_count = 0;
3415     char *retlist = NULL;
3416     char *savelist;
3417     int i;
3418     const char *topo_name;
3419     unsigned char *p_blob;
3420     int n_bytes;
3421     gaiaGeomCollPtr linestring = NULL;
3422     gaiaLinestringPtr ln;
3423     double tolerance = -1;
3424     int invalid = 0;
3425     struct gaia_topology *topo;
3426     GaiaTopologyAccessorPtr accessor = NULL;
3427     int gpkg_amphibious = 0;
3428     int gpkg_mode = 0;
3429     sqlite3_context *context = (sqlite3_context *) xcontext;
3430     sqlite3_value **argv = (sqlite3_value **) xargv;
3431     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3432     struct splite_internal_cache *cache = sqlite3_user_data (context);
3433     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3434     if (cache != NULL)
3435       {
3436 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
3437 	  gpkg_mode = cache->gpkg_mode;
3438       }
3439     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3440 	goto null_arg;
3441     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3442 	topo_name = (const char *) sqlite3_value_text (argv[0]);
3443     else
3444 	goto invalid_arg;
3445     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3446 	goto null_arg;
3447     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
3448       {
3449 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
3450 	  n_bytes = sqlite3_value_bytes (argv[1]);
3451       }
3452     else
3453 	goto invalid_arg;
3454     if (argc >= 3)
3455       {
3456 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3457 	      goto null_arg;
3458 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
3459 	    {
3460 		int t = sqlite3_value_int (argv[2]);
3461 		tolerance = t;
3462 	    }
3463 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
3464 	      tolerance = sqlite3_value_double (argv[2]);
3465 	  else
3466 	      goto invalid_arg;
3467 	  if (tolerance < 0.0)
3468 	      goto negative_tolerance;
3469       }
3470 
3471 /* attempting to get a Linestring Geometry */
3472     linestring =
3473 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
3474 				     gpkg_amphibious);
3475     if (!linestring)
3476 	goto invalid_arg;
3477     if (linestring->FirstPoint != NULL)
3478 	invalid = 1;
3479     if (linestring->FirstPolygon != NULL)
3480 	invalid = 1;
3481     if (linestring->FirstLinestring == NULL)
3482 	invalid = 1;
3483     if (invalid)
3484 	goto invalid_arg;
3485 
3486 /* attempting to get a Topology Accessor */
3487     accessor = gaiaGetTopology (sqlite, cache, topo_name);
3488     if (accessor == NULL)
3489 	goto no_topo;
3490     gaiatopo_reset_last_error_msg (accessor);
3491     topo = (struct gaia_topology *) accessor;
3492     if (!check_matching_srid_dims
3493 	(accessor, linestring->Srid, linestring->DimensionModel))
3494 	goto invalid_geom;
3495 
3496     start_topo_savepoint (sqlite, cache);
3497 
3498 /* removing any existing Face except the Universal one */
3499     if (kill_all_existing_faces (sqlite, topo->topology_name) == 0)
3500       {
3501 	  msg = "TopoGeo_AddLineStringNoFace: unable to remove existing Faces";
3502 	  gaiatopo_set_last_error_msg (accessor, msg);
3503 	  sqlite3_result_error (context, msg, -1);
3504 	  return;
3505       }
3506 
3507     ln = linestring->FirstLinestring;
3508     while (ln != NULL)
3509       {
3510 	  /* looping on individual Linestrings */
3511 	  ret =
3512 	      gaiaTopoGeo_AddLineStringNoFace (accessor, ln, tolerance,
3513 					       &edge_ids, &ids_count);
3514 	  if (!ret)
3515 	      break;
3516 	  for (i = 0; i < ids_count; i++)
3517 	    {
3518 		sprintf (xedge_id, "%lld", edge_ids[i]);
3519 		if (retlist == NULL)
3520 		    retlist = sqlite3_mprintf ("%s", xedge_id);
3521 		else
3522 		  {
3523 		      savelist = retlist;
3524 		      retlist = sqlite3_mprintf ("%s, %s", savelist, xedge_id);
3525 		      sqlite3_free (savelist);
3526 		  }
3527 	    }
3528 	  free (edge_ids);
3529 	  ln = ln->Next;
3530       }
3531 
3532     if (!ret)
3533 	rollback_topo_savepoint (sqlite, cache);
3534     else
3535 	release_topo_savepoint (sqlite, cache);
3536     gaiaFreeGeomColl (linestring);
3537     linestring = NULL;
3538     if (!ret)
3539       {
3540 	  msg = gaiaGetRtTopoErrorMsg (cache);
3541 	  gaiatopo_set_last_error_msg (accessor, msg);
3542 	  sqlite3_result_error (context, msg, -1);
3543 	  sqlite3_free (retlist);
3544 	  return;
3545       }
3546     sqlite3_result_text (context, retlist, strlen (retlist), sqlite3_free);
3547     return;
3548 
3549   no_topo:
3550     if (linestring != NULL)
3551 	gaiaFreeGeomColl (linestring);
3552     msg = "SQL/MM Spatial exception - invalid topology name.";
3553     gaiatopo_set_last_error_msg (accessor, msg);
3554     sqlite3_result_error (context, msg, -1);
3555     return;
3556 
3557   null_arg:
3558     if (linestring != NULL)
3559 	gaiaFreeGeomColl (linestring);
3560     msg = "SQL/MM Spatial exception - null argument.";
3561     gaiatopo_set_last_error_msg (accessor, msg);
3562     sqlite3_result_error (context, msg, -1);
3563     return;
3564 
3565   invalid_arg:
3566     if (linestring != NULL)
3567 	gaiaFreeGeomColl (linestring);
3568     msg = "SQL/MM Spatial exception - invalid argument.";
3569     gaiatopo_set_last_error_msg (accessor, msg);
3570     sqlite3_result_error (context, msg, -1);
3571     return;
3572 
3573   invalid_geom:
3574     if (linestring != NULL)
3575 	gaiaFreeGeomColl (linestring);
3576     msg =
3577 	"SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).";
3578     gaiatopo_set_last_error_msg (accessor, msg);
3579     sqlite3_result_error (context, msg, -1);
3580     return;
3581 
3582   negative_tolerance:
3583     if (linestring != NULL)
3584 	gaiaFreeGeomColl (linestring);
3585     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
3586     gaiatopo_set_last_error_msg (accessor, msg);
3587     sqlite3_result_error (context, msg, -1);
3588     return;
3589 }
3590 
3591 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_Polygonize(const void * xcontext,int argc,const void * xargv)3592 fnctaux_TopoGeo_Polygonize (const void *xcontext, int argc, const void *xargv)
3593 {
3594 /* SQL function:
3595 / TopoGeo_Polygonize ( text topology-name )
3596 /
3597 / TopoGeo_Polygonize ( text topology-name , int force-rebuild )
3598 */
3599     int edgesCount = 0;
3600     const char *msg;
3601     int ret;
3602     const char *topo_name;
3603     int force_rebuild = 0;
3604     struct gaia_topology *topo;
3605     GaiaTopologyAccessorPtr accessor = NULL;
3606     sqlite3_context *context = (sqlite3_context *) xcontext;
3607     sqlite3_value **argv = (sqlite3_value **) xargv;
3608     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3609     struct splite_internal_cache *cache = sqlite3_user_data (context);
3610     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3611     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3612 	goto null_arg;
3613     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3614 	topo_name = (const char *) sqlite3_value_text (argv[0]);
3615     else
3616 	goto invalid_arg;
3617     if (argc >= 2)
3618       {
3619 	  if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3620 	      goto null_arg;
3621 	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
3622 	      force_rebuild = sqlite3_value_int (argv[1]);
3623 	  else
3624 	      goto invalid_arg;
3625       }
3626 
3627 /* attempting to get a Topology Accessor */
3628     accessor = gaiaGetTopology (sqlite, cache, topo_name);
3629     if (accessor == NULL)
3630 	goto no_topo;
3631     gaiatopo_reset_last_error_msg (accessor);
3632     topo = (struct gaia_topology *) accessor;
3633 
3634 /* testing if there are unreferenced Edges */
3635     edgesCount = test_inconsistent_topology (accessor);
3636     if (edgesCount < 0)
3637       {
3638 	  msg = "TopoGeo_Polygonize: unable to check Topology consistency";
3639 	  gaiatopo_set_last_error_msg (accessor, msg);
3640 	  sqlite3_result_error (context, msg, -1);
3641 	  return;
3642       }
3643     if (!edgesCount)
3644       {
3645 	  if (!force_rebuild)
3646 	    {
3647 		sqlite3_result_null (context);
3648 		return;
3649 	    }
3650       }
3651 
3652     start_topo_savepoint (sqlite, cache);
3653 
3654 /* removing any existing Face except the Universal one */
3655     if (kill_all_existing_faces (sqlite, topo->topology_name) == 0)
3656       {
3657 	  msg = "TopoGeo_Polygonize: unable to remove existing Faces";
3658 	  gaiatopo_set_last_error_msg (accessor, msg);
3659 	  sqlite3_result_error (context, msg, -1);
3660 	  return;
3661       }
3662 
3663     ret = gaiaTopoGeo_Polygonize (accessor);
3664 
3665     if (!ret)
3666 	rollback_topo_savepoint (sqlite, cache);
3667     else
3668 	release_topo_savepoint (sqlite, cache);
3669     if (!ret)
3670       {
3671 	  msg = gaiaGetRtTopoErrorMsg (cache);
3672 	  gaiatopo_set_last_error_msg (accessor, msg);
3673 	  sqlite3_result_error (context, msg, -1);
3674 	  return;
3675       }
3676     sqlite3_result_null (context);
3677     return;
3678 
3679   no_topo:
3680     msg = "SQL/MM Spatial exception - invalid topology name.";
3681     gaiatopo_set_last_error_msg (accessor, msg);
3682     sqlite3_result_error (context, msg, -1);
3683     return;
3684 
3685   null_arg:
3686     msg = "SQL/MM Spatial exception - null argument.";
3687     gaiatopo_set_last_error_msg (accessor, msg);
3688     sqlite3_result_error (context, msg, -1);
3689     return;
3690 
3691   invalid_arg:
3692     msg = "SQL/MM Spatial exception - invalid argument.";
3693     gaiatopo_set_last_error_msg (accessor, msg);
3694     sqlite3_result_error (context, msg, -1);
3695     return;
3696 }
3697 
3698 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_TopoSnap(const void * xcontext,int argc,const void * xargv)3699 fnctaux_TopoGeo_TopoSnap (const void *xcontext, int argc, const void *xargv)
3700 {
3701 /* SQL function:
3702 / TopoGeo_TopoSnap ( text topology-name, Geometry geom, int iterate )
3703 / TopoGeo_TopoSnap ( text topology-name, Geometry geom, double tolerance_snap,
3704 /                    double tolerance_removal, int iterate )
3705 /
3706 / returns: the snapped Geometry
3707 / raises an exception on failure
3708 */
3709     const char *msg;
3710     const char *topo_name;
3711     unsigned char *p_blob;
3712     int n_bytes;
3713     gaiaGeomCollPtr geom = NULL;
3714     gaiaGeomCollPtr g2;
3715     int iterate;
3716     double tolerance_snap = -1;
3717     double tolerance_removal = -1;
3718     GaiaTopologyAccessorPtr accessor = NULL;
3719     int gpkg_amphibious = 0;
3720     int gpkg_mode = 0;
3721     int tiny_point = 0;
3722     sqlite3_context *context = (sqlite3_context *) xcontext;
3723     sqlite3_value **argv = (sqlite3_value **) xargv;
3724     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3725     struct splite_internal_cache *cache = sqlite3_user_data (context);
3726     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3727     if (cache != NULL)
3728       {
3729 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
3730 	  gpkg_mode = cache->gpkg_mode;
3731 	  tiny_point = cache->tinyPointEnabled;
3732       }
3733     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3734 	goto null_arg;
3735     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3736 	topo_name = (const char *) sqlite3_value_text (argv[0]);
3737     else
3738 	goto invalid_arg;
3739     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3740 	goto null_arg;
3741     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
3742       {
3743 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
3744 	  n_bytes = sqlite3_value_bytes (argv[1]);
3745       }
3746     else
3747 	goto invalid_arg;
3748     if (argc == 3)
3749       {
3750 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3751 	      goto null_arg;
3752 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
3753 	      iterate = sqlite3_value_int (argv[2]);
3754 	  else
3755 	      goto invalid_arg;
3756       }
3757     if (argc >= 5)
3758       {
3759 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3760 	      goto null_arg;
3761 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
3762 	    {
3763 		int t = sqlite3_value_int (argv[2]);
3764 		tolerance_snap = t;
3765 	    }
3766 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
3767 	      tolerance_snap = sqlite3_value_double (argv[2]);
3768 	  else
3769 	      goto invalid_arg;
3770 	  if (tolerance_snap < 0.0)
3771 	      goto negative_tolerance;
3772 	  if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
3773 	      goto skip_negative;
3774 	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
3775 	    {
3776 		int t = sqlite3_value_int (argv[3]);
3777 		tolerance_removal = t;
3778 	    }
3779 	  else if (sqlite3_value_type (argv[3]) == SQLITE_FLOAT)
3780 	      tolerance_removal = sqlite3_value_double (argv[3]);
3781 	  else
3782 	      goto invalid_arg;
3783 	  if (tolerance_removal < 0.0)
3784 	      goto negative_tolerance;
3785 	skip_negative:
3786 	  if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
3787 	      goto null_arg;
3788 	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
3789 	      iterate = sqlite3_value_int (argv[4]);
3790 	  else
3791 	      goto invalid_arg;
3792       }
3793 
3794 /* attempting to get a Geometry */
3795     geom =
3796 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
3797 				     gpkg_amphibious);
3798     if (!geom)
3799 	goto invalid_arg;
3800 
3801 /* attempting to get a Topology Accessor */
3802     accessor = gaiaGetTopology (sqlite, cache, topo_name);
3803     if (accessor == NULL)
3804 	goto no_topo;
3805 
3806     gaiatopo_reset_last_error_msg (accessor);
3807     g2 = gaiaTopoSnap (accessor, geom, tolerance_snap, tolerance_removal,
3808 		       iterate);
3809     gaiaFreeGeomColl (geom);
3810     if (g2 == NULL)
3811       {
3812 	  msg = gaiaGetRtTopoErrorMsg (cache);
3813 	  if (msg != NULL)
3814 	    {
3815 		gaiatopo_set_last_error_msg (accessor, msg);
3816 		sqlite3_result_error (context, msg, -1);
3817 		return;
3818 	    }
3819 	  sqlite3_result_null (context);
3820 	  return;
3821       }
3822     gaiaToSpatiaLiteBlobWkbEx2 (g2, &p_blob, &n_bytes, gpkg_mode, tiny_point);
3823     gaiaFreeGeomColl (g2);
3824     if (p_blob == NULL)
3825 	sqlite3_result_null (context);
3826     else
3827 	sqlite3_result_blob (context, p_blob, n_bytes, free);
3828     return;
3829 
3830   no_topo:
3831     if (geom != NULL)
3832 	gaiaFreeGeomColl (geom);
3833     msg = "SQL/MM Spatial exception - invalid topology name.";
3834     gaiatopo_set_last_error_msg (accessor, msg);
3835     sqlite3_result_error (context, msg, -1);
3836     return;
3837 
3838   null_arg:
3839     if (geom != NULL)
3840 	gaiaFreeGeomColl (geom);
3841     msg = "SQL/MM Spatial exception - null argument.";
3842     gaiatopo_set_last_error_msg (accessor, msg);
3843     sqlite3_result_error (context, msg, -1);
3844     return;
3845 
3846   invalid_arg:
3847     if (geom != NULL)
3848 	gaiaFreeGeomColl (geom);
3849     msg = "SQL/MM Spatial exception - invalid argument.";
3850     gaiatopo_set_last_error_msg (accessor, msg);
3851     sqlite3_result_error (context, msg, -1);
3852     return;
3853 
3854   negative_tolerance:
3855     if (geom != NULL)
3856 	gaiaFreeGeomColl (geom);
3857     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
3858     gaiatopo_set_last_error_msg (accessor, msg);
3859     sqlite3_result_error (context, msg, -1);
3860     return;
3861 }
3862 
3863 static int
check_input_geo_table(sqlite3 * sqlite,const char * db_prefix,const char * table,const char * column,char ** xtable,char ** xcolumn,int * srid,int * family,int * dims)3864 check_input_geo_table (sqlite3 * sqlite, const char *db_prefix,
3865 		       const char *table, const char *column, char **xtable,
3866 		       char **xcolumn, int *srid, int *family, int *dims)
3867 {
3868 /* checking if an input GeoTable do really exist */
3869     int ret;
3870     int i;
3871     char **results;
3872     int rows;
3873     int columns;
3874     char *errMsg = NULL;
3875     char *sql;
3876     char *xprefix;
3877     int len;
3878     int count = 0;
3879     char *xx_table = NULL;
3880     char *xx_column = NULL;
3881     char *ztable;
3882     int xtype;
3883     int xfamily;
3884     int xdims;
3885     int xsrid;
3886 
3887     *xtable = NULL;
3888     *xcolumn = NULL;
3889     *srid = -1;
3890     *dims = GAIA_XY;
3891 
3892 /* querying GEOMETRY_COLUMNS */
3893     xprefix = gaiaDoubleQuotedSql (db_prefix);
3894     if (column == NULL)
3895 	sql =
3896 	    sqlite3_mprintf
3897 	    ("SELECT f_table_name, f_geometry_column, geometry_type, srid "
3898 	     "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q)",
3899 	     xprefix, table);
3900     else
3901 	sql =
3902 	    sqlite3_mprintf
3903 	    ("SELECT f_table_name, f_geometry_column, geometry_type, srid "
3904 	     "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q) AND "
3905 	     "Lower(f_geometry_column) = Lower(%Q)", xprefix, table, column);
3906     free (xprefix);
3907     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
3908     sqlite3_free (sql);
3909     if (ret != SQLITE_OK)
3910       {
3911 	  sqlite3_free (errMsg);
3912 	  return 0;
3913       }
3914     for (i = 1; i <= rows; i++)
3915       {
3916 	  const char *table_name = results[(i * columns) + 0];
3917 	  const char *column_name = results[(i * columns) + 1];
3918 	  xtype = atoi (results[(i * columns) + 2]);
3919 	  xsrid = atoi (results[(i * columns) + 3]);
3920 	  len = strlen (table_name);
3921 	  if (xx_table != NULL)
3922 	      free (xx_table);
3923 	  xx_table = malloc (len + 1);
3924 	  strcpy (xx_table, table_name);
3925 	  len = strlen (column_name);
3926 	  if (xx_column != NULL)
3927 	      free (xx_column);
3928 	  xx_column = malloc (len + 1);
3929 	  strcpy (xx_column, column_name);
3930 	  count++;
3931       }
3932     sqlite3_free_table (results);
3933 
3934     if (count != 1)
3935       {
3936 	  if (xx_table != NULL)
3937 	      free (xx_table);
3938 	  if (xx_column != NULL)
3939 	      free (xx_column);
3940 	  return 0;
3941       }
3942 
3943 /* testing if the GeoTable do really exist */
3944     count = 0;
3945     xprefix = gaiaDoubleQuotedSql (db_prefix);
3946     ztable = gaiaDoubleQuotedSql (xx_table);
3947     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, ztable);
3948     free (xprefix);
3949     free (ztable);
3950     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
3951     sqlite3_free (sql);
3952     if (ret != SQLITE_OK)
3953       {
3954 	  sqlite3_free (errMsg);
3955 	  return 0;
3956       }
3957     for (i = 1; i <= rows; i++)
3958       {
3959 	  const char *column_name = results[(i * columns) + 1];
3960 	  if (strcasecmp (column_name, xx_column) == 0)
3961 	      count++;
3962       }
3963     sqlite3_free_table (results);
3964 
3965     if (count != 1)
3966       {
3967 	  if (xx_table != NULL)
3968 	      free (xx_table);
3969 	  if (xx_column != NULL)
3970 	      free (xx_column);
3971 	  return 0;
3972       }
3973 
3974     switch (xtype)
3975       {
3976       case 0:
3977       case 1:
3978       case 2:
3979       case 3:
3980       case 4:
3981       case 5:
3982       case 6:
3983       case 7:
3984 	  xdims = GAIA_XY;
3985 	  break;
3986       case 1000:
3987       case 1001:
3988       case 1002:
3989       case 1003:
3990       case 1004:
3991       case 1005:
3992       case 1006:
3993       case 1007:
3994 	  xdims = GAIA_XY_Z;
3995 	  break;
3996       case 2000:
3997       case 2001:
3998       case 2002:
3999       case 2003:
4000       case 2004:
4001       case 2005:
4002       case 2006:
4003       case 2007:
4004 	  xdims = GAIA_XY_M;
4005 	  break;
4006       case 3000:
4007       case 3001:
4008       case 3002:
4009       case 3003:
4010       case 3004:
4011       case 3005:
4012       case 3006:
4013       case 3007:
4014 	  xdims = GAIA_XY_Z_M;
4015 	  break;
4016       };
4017     switch (xtype)
4018       {
4019       case 1:
4020       case 1001:
4021       case 2001:
4022       case 3001:
4023       case 4:
4024       case 1004:
4025       case 2004:
4026       case 3004:
4027 	  xfamily = GAIA_TYPE_POINT;
4028 	  break;
4029       case 2:
4030       case 1002:
4031       case 2002:
4032       case 3002:
4033       case 5:
4034       case 1005:
4035       case 2005:
4036       case 3005:
4037 	  xfamily = GAIA_TYPE_LINESTRING;
4038 	  break;
4039       case 3:
4040       case 1003:
4041       case 2003:
4042       case 3003:
4043       case 6:
4044       case 1006:
4045       case 2006:
4046       case 3006:
4047 	  xfamily = GAIA_TYPE_POLYGON;
4048 	  break;
4049       default:
4050 	  xfamily = GAIA_TYPE_NONE;
4051 	  break;
4052       };
4053     *xtable = xx_table;
4054     *xcolumn = xx_column;
4055     *srid = xsrid;
4056     *family = xfamily;
4057     *dims = xdims;
4058     return 1;
4059 }
4060 
4061 static int
check_output_geo_table(sqlite3 * sqlite,const char * table)4062 check_output_geo_table (sqlite3 * sqlite, const char *table)
4063 {
4064 /* checking if an output GeoTable do already exist */
4065     int ret;
4066     int i;
4067     char **results;
4068     int rows;
4069     int columns;
4070     char *errMsg = NULL;
4071     char *sql;
4072     int count = 0;
4073     char *ztable;
4074 
4075 /* querying GEOMETRY_COLUMNS */
4076     sql =
4077 	sqlite3_mprintf
4078 	("SELECT f_table_name, f_geometry_column "
4079 	 "FROM MAIN.geometry_columns WHERE Lower(f_table_name) = Lower(%Q)",
4080 	 table);
4081     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
4082     sqlite3_free (sql);
4083     if (ret != SQLITE_OK)
4084       {
4085 	  sqlite3_free (errMsg);
4086 	  return 0;
4087       }
4088     for (i = 1; i <= rows; i++)
4089 	count++;
4090     sqlite3_free_table (results);
4091 
4092     if (count != 0)
4093 	return 0;
4094 
4095 /* testing if the Table already exist */
4096     count = 0;
4097     ztable = gaiaDoubleQuotedSql (table);
4098     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", ztable);
4099     free (ztable);
4100     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
4101     sqlite3_free (sql);
4102     if (ret != SQLITE_OK)
4103       {
4104 	  sqlite3_free (errMsg);
4105 	  return 0;
4106       }
4107     for (i = 1; i <= rows; i++)
4108 	count++;
4109     sqlite3_free_table (results);
4110 
4111     if (count != 0)
4112 	return 0;
4113     return 1;
4114 }
4115 
4116 SPATIALITE_PRIVATE int
gaia_check_output_table(const void * handle,const char * table)4117 gaia_check_output_table (const void *handle, const char *table)
4118 {
4119 /* checking if an output Table do already exist */
4120     sqlite3 *sqlite = (sqlite3 *) handle;
4121     int ret;
4122     int i;
4123     char **results;
4124     int rows;
4125     int columns;
4126     char *errMsg = NULL;
4127     char *sql;
4128     int count = 0;
4129     char *ztable;
4130 
4131 /* testing if the Table already exist */
4132     ztable = gaiaDoubleQuotedSql (table);
4133     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", ztable);
4134     free (ztable);
4135     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
4136     sqlite3_free (sql);
4137     if (ret != SQLITE_OK)
4138       {
4139 	  sqlite3_free (errMsg);
4140 	  return 0;
4141       }
4142     for (i = 1; i <= rows; i++)
4143 	count++;
4144     sqlite3_free_table (results);
4145 
4146     if (count != 0)
4147 	return 0;
4148     return 1;
4149 }
4150 
4151 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_SnappedGeoTable(const void * xcontext,int argc,const void * xargv)4152 fnctaux_TopoGeo_SnappedGeoTable (const void *xcontext, int argc,
4153 				 const void *xargv)
4154 {
4155 /* SQL function:
4156 / TopoGeo_SnappedGeoTable ( text topology-name, text db-prefix, text table,
4157 /                           text column, text outtable, int iterate )
4158 / TopoGeo_SnappedGeoTable ( text topology-name, text db-prefix, text table,
4159 /                           text column, text outtable, double tolerance_snap,
4160 /                           double tolerance_removal, int iterate )
4161 /
4162 / returns: 1 on success
4163 / raises an exception on failure
4164 */
4165     int ret;
4166     const char *msg;
4167     const char *topo_name;
4168     const char *db_prefix;
4169     const char *table;
4170     const char *column;
4171     const char *outtable;
4172     char *xtable = NULL;
4173     char *xcolumn = NULL;
4174     int iterate;
4175     double tolerance_snap = -1;
4176     double tolerance_removal = -1;
4177     int srid;
4178     int family;
4179     int dims;
4180     GaiaTopologyAccessorPtr accessor = NULL;
4181     sqlite3_context *context = (sqlite3_context *) xcontext;
4182     sqlite3_value **argv = (sqlite3_value **) xargv;
4183     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4184     struct splite_internal_cache *cache = sqlite3_user_data (context);
4185     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4186     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4187 	goto null_arg;
4188     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4189 	topo_name = (const char *) sqlite3_value_text (argv[0]);
4190     else
4191 	goto invalid_arg;
4192     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4193 	db_prefix = "main";
4194     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
4195 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
4196     else
4197 	goto invalid_arg;
4198     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
4199 	goto null_arg;
4200     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
4201 	table = (const char *) sqlite3_value_text (argv[2]);
4202     else
4203 	goto invalid_arg;
4204     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
4205 	column = NULL;
4206     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
4207 	column = (const char *) sqlite3_value_text (argv[3]);
4208     else
4209 	goto invalid_arg;
4210     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
4211 	goto null_arg;
4212     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
4213 	outtable = (const char *) sqlite3_value_text (argv[4]);
4214     else
4215 	goto invalid_arg;
4216     if (argc == 6)
4217       {
4218 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
4219 	      goto null_arg;
4220 	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
4221 	      iterate = sqlite3_value_int (argv[5]);
4222 	  else
4223 	      goto invalid_arg;
4224       }
4225     if (argc >= 8)
4226       {
4227 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
4228 	      goto null_arg;
4229 	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
4230 	    {
4231 		int t = sqlite3_value_int (argv[5]);
4232 		tolerance_snap = t;
4233 	    }
4234 	  else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
4235 	      tolerance_snap = sqlite3_value_double (argv[5]);
4236 	  else
4237 	      goto invalid_arg;
4238 	  if (tolerance_snap < 0.0)
4239 	      goto negative_tolerance;
4240 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
4241 	      goto negative_skip;
4242 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
4243 	    {
4244 		int t = sqlite3_value_int (argv[6]);
4245 		tolerance_removal = t;
4246 	    }
4247 	  else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
4248 	      tolerance_removal = sqlite3_value_double (argv[6]);
4249 	  else
4250 	      goto invalid_arg;
4251 	  if (tolerance_removal < 0.0)
4252 	      goto negative_tolerance;
4253 	negative_skip:
4254 	  if (sqlite3_value_type (argv[7]) == SQLITE_NULL)
4255 	      goto null_arg;
4256 	  else if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
4257 	      iterate = sqlite3_value_int (argv[7]);
4258 	  else
4259 	      goto invalid_arg;
4260       }
4261 
4262 /* attempting to get a Topology Accessor */
4263     accessor = gaiaGetTopology (sqlite, cache, topo_name);
4264     if (accessor == NULL)
4265 	goto no_topo;
4266     gaiatopo_reset_last_error_msg (accessor);
4267 
4268 /* checking the input GeoTable */
4269     if (!check_input_geo_table
4270 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &family,
4271 	 &dims))
4272 	goto no_input;
4273     if (!check_matching_srid_dims (accessor, srid, dims))
4274 	goto invalid_geom;
4275 
4276 /* checking the output GeoTable */
4277     if (!check_output_geo_table (sqlite, outtable))
4278 	goto err_output;
4279 
4280     start_topo_savepoint (sqlite, cache);
4281     ret =
4282 	gaiaTopoGeo_SnappedGeoTable (accessor, db_prefix, xtable, xcolumn,
4283 				     outtable, tolerance_snap,
4284 				     tolerance_removal, iterate);
4285     if (!ret)
4286 	rollback_topo_savepoint (sqlite, cache);
4287     else
4288 	release_topo_savepoint (sqlite, cache);
4289     free (xtable);
4290     free (xcolumn);
4291     if (!ret)
4292       {
4293 	  msg = gaiaGetRtTopoErrorMsg (cache);
4294 	  gaiatopo_set_last_error_msg (accessor, msg);
4295 	  sqlite3_result_error (context, msg, -1);
4296 	  return;
4297       }
4298     sqlite3_result_int (context, 1);
4299     return;
4300 
4301   no_topo:
4302     if (xtable != NULL)
4303 	free (xtable);
4304     if (xcolumn != NULL)
4305 	free (xcolumn);
4306     msg = "SQL/MM Spatial exception - invalid topology name.";
4307     gaiatopo_set_last_error_msg (accessor, msg);
4308     sqlite3_result_error (context, msg, -1);
4309     return;
4310 
4311   no_input:
4312     if (xtable != NULL)
4313 	free (xtable);
4314     if (xcolumn != NULL)
4315 	free (xcolumn);
4316     msg = "SQL/MM Spatial exception - invalid input GeoTable.";
4317     gaiatopo_set_last_error_msg (accessor, msg);
4318     sqlite3_result_error (context, msg, -1);
4319     return;
4320 
4321   err_output:
4322     if (xtable != NULL)
4323 	free (xtable);
4324     if (xcolumn != NULL)
4325 	free (xcolumn);
4326     msg = "TopoGeo_SnappedGeoTable: output GeoTable already exists.";
4327     gaiatopo_set_last_error_msg (accessor, msg);
4328     sqlite3_result_error (context, msg, -1);
4329     return;
4330 
4331   null_arg:
4332     if (xtable != NULL)
4333 	free (xtable);
4334     if (xcolumn != NULL)
4335 	free (xcolumn);
4336     msg = "SQL/MM Spatial exception - null argument.";
4337     gaiatopo_set_last_error_msg (accessor, msg);
4338     sqlite3_result_error (context, msg, -1);
4339     return;
4340 
4341   invalid_arg:
4342     if (xtable != NULL)
4343 	free (xtable);
4344     if (xcolumn != NULL)
4345 	free (xcolumn);
4346     msg = "SQL/MM Spatial exception - invalid argument.";
4347     gaiatopo_set_last_error_msg (accessor, msg);
4348     sqlite3_result_error (context, msg, -1);
4349     return;
4350 
4351   invalid_geom:
4352     if (xtable != NULL)
4353 	free (xtable);
4354     if (xcolumn != NULL)
4355 	free (xcolumn);
4356     msg =
4357 	"SQL/MM Spatial exception - invalid GeoTable (mismatching SRID or dimensions).";
4358     gaiatopo_set_last_error_msg (accessor, msg);
4359     sqlite3_result_error (context, msg, -1);
4360     return;
4361 
4362   negative_tolerance:
4363     if (xtable != NULL)
4364 	free (xtable);
4365     if (xcolumn != NULL)
4366 	free (xcolumn);
4367     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
4368     gaiatopo_set_last_error_msg (accessor, msg);
4369     sqlite3_result_error (context, msg, -1);
4370     return;
4371 }
4372 
4373 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_SubdivideLines(const void * xcontext,int argc,const void * xargv)4374 fnctaux_TopoGeo_SubdivideLines (const void *xcontext, int argc,
4375 				const void *xargv)
4376 {
4377 /* SQL function:
4378 / TopoGeo_SubdivideLines ( Geometry geom, int line_max_points )
4379 / TopoGeo_SubdivideLines ( Geometry geom, int line_max_points,
4380 /                          double max_length )
4381 /
4382 / returns: a MultiLinestring
4383 / raises an exception on failure
4384 */
4385     const char *msg;
4386     unsigned char *p_blob;
4387     int n_bytes;
4388     gaiaGeomCollPtr geom;
4389     gaiaGeomCollPtr result;
4390     int line_max_points = -1;
4391     double max_length = -1.0;
4392     int gpkg_amphibious = 0;
4393     int gpkg_mode = 0;
4394     int tiny_point = 0;
4395     sqlite3_context *context = (sqlite3_context *) xcontext;
4396     sqlite3_value **argv = (sqlite3_value **) xargv;
4397     struct splite_internal_cache *cache = sqlite3_user_data (context);
4398     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4399     if (cache != NULL)
4400       {
4401 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
4402 	  gpkg_mode = cache->gpkg_mode;
4403 	  tiny_point = cache->tinyPointEnabled;
4404       }
4405     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4406 	goto null_arg;
4407     else if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
4408       {
4409 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
4410 	  n_bytes = sqlite3_value_bytes (argv[0]);
4411       }
4412     else
4413 	goto invalid_arg;
4414     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4415 	;
4416     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
4417       {
4418 	  line_max_points = sqlite3_value_int (argv[1]);
4419 	  if (line_max_points < 2)
4420 	      goto illegal_max_points;
4421       }
4422     else
4423 	goto invalid_arg;
4424     if (argc >= 3)
4425       {
4426 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
4427 	      ;
4428 	  else
4429 	    {
4430 		if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
4431 		  {
4432 		      int max = sqlite3_value_int (argv[2]);
4433 		      max_length = max;
4434 		  }
4435 		else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
4436 		    max_length = sqlite3_value_int (argv[2]);
4437 		else
4438 		    goto invalid_arg;
4439 		if (max_length <= 0.0)
4440 		    goto nonpositive_max_length;
4441 	    }
4442       }
4443 
4444 /* attempting to get a Geometry */
4445     geom =
4446 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
4447 				     gpkg_amphibious);
4448     if (!geom)
4449 	goto invalid_geom;
4450 
4451 /* splitting the geometry */
4452     result = gaiaTopoGeo_SubdivideLines (geom, line_max_points, max_length);
4453     gaiaFreeGeomColl (geom);
4454     if (result == NULL)
4455 	goto invalid_geom;
4456     gaiaToSpatiaLiteBlobWkbEx2 (result, &p_blob, &n_bytes, gpkg_mode,
4457 				tiny_point);
4458     gaiaFreeGeomColl (result);
4459     if (p_blob == NULL)
4460 	goto invalid_geom;
4461     else
4462 	sqlite3_result_blob (context, p_blob, n_bytes, free);
4463     return;
4464 
4465   null_arg:
4466     msg = "SQL/MM Spatial exception - null argument.";
4467     sqlite3_result_error (context, msg, -1);
4468     return;
4469 
4470   invalid_arg:
4471     msg = "SQL/MM Spatial exception - invalid argument.";
4472     sqlite3_result_error (context, msg, -1);
4473     return;
4474 
4475   invalid_geom:
4476     msg = "SQL/MM Spatial exception - invalid Geometry.";
4477     sqlite3_result_error (context, msg, -1);
4478     return;
4479 
4480   illegal_max_points:
4481     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
4482     sqlite3_result_error (context, msg, -1);
4483     return;
4484 
4485   nonpositive_max_length:
4486     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
4487     sqlite3_result_error (context, msg, -1);
4488     return;
4489 }
4490 
4491 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_FromGeoTable(const void * xcontext,int argc,const void * xargv)4492 fnctaux_TopoGeo_FromGeoTable (const void *xcontext, int argc, const void *xargv)
4493 {
4494 /* SQL function:
4495 / TopoGeo_FromGeoTable ( text topology-name, text db-prefix, text table,
4496 /                        text column )
4497 / TopoGeo_FromGeoTable ( text topology-name, text db-prefix, text table,
4498 /                        text column, int line_max_points )
4499 / TopoGeo_FromGeoTable ( text topology-name, text db-prefix, text table,
4500 /                        text column, int line_max_points, double max_length )
4501 / TopoGeo_FromGeoTable ( text topology-name, text db-prefix, text table,
4502 /                        text column, int line_max_points, double max_length,
4503 /                        double tolerance )
4504 /
4505 / returns: 1 on success
4506 / raises an exception on failure
4507 */
4508     const char *msg;
4509     int ret;
4510     const char *topo_name;
4511     const char *db_prefix;
4512     const char *table;
4513     const char *column;
4514     char *xtable = NULL;
4515     char *xcolumn = NULL;
4516     int srid;
4517     int family;
4518     int dims;
4519     int line_max_points = -1;
4520     double max_length = -1.0;
4521     double tolerance = -1;
4522     GaiaTopologyAccessorPtr accessor = NULL;
4523     sqlite3_context *context = (sqlite3_context *) xcontext;
4524     sqlite3_value **argv = (sqlite3_value **) xargv;
4525     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4526     struct splite_internal_cache *cache = sqlite3_user_data (context);
4527     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4528     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4529 	goto null_arg;
4530     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4531 	topo_name = (const char *) sqlite3_value_text (argv[0]);
4532     else
4533 	goto invalid_arg;
4534     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4535 	db_prefix = "main";
4536     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
4537 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
4538     else
4539 	goto invalid_arg;
4540     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
4541 	goto null_arg;
4542     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
4543 	table = (const char *) sqlite3_value_text (argv[2]);
4544     else
4545 	goto invalid_arg;
4546     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
4547 	column = NULL;
4548     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
4549 	column = (const char *) sqlite3_value_text (argv[3]);
4550     else
4551 	goto invalid_arg;
4552     if (argc >= 5)
4553       {
4554 	  if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
4555 	      ;
4556 	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
4557 	    {
4558 		line_max_points = sqlite3_value_int (argv[4]);
4559 		if (line_max_points < 2)
4560 		    goto illegal_max_points;
4561 	    }
4562 	  else
4563 	      goto invalid_arg;
4564       }
4565     if (argc >= 6)
4566       {
4567 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
4568 	      ;
4569 	  else
4570 	    {
4571 		if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
4572 		  {
4573 		      int max = sqlite3_value_int (argv[5]);
4574 		      max_length = max;
4575 		  }
4576 		else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
4577 		    max_length = sqlite3_value_double (argv[5]);
4578 		else
4579 		    goto invalid_arg;
4580 		if (max_length <= 0.0)
4581 		    goto nonpositive_max_length;
4582 	    }
4583       }
4584     if (argc >= 7)
4585       {
4586 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
4587 	      goto null_arg;
4588 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
4589 	    {
4590 		int t = sqlite3_value_int (argv[6]);
4591 		tolerance = t;
4592 	    }
4593 	  else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
4594 	      tolerance = sqlite3_value_double (argv[6]);
4595 	  else
4596 	      goto invalid_arg;
4597 	  if (tolerance < 0.0)
4598 	      goto negative_tolerance;
4599       }
4600 
4601 /* attempting to get a Topology Accessor */
4602     accessor = gaiaGetTopology (sqlite, cache, topo_name);
4603     if (accessor == NULL)
4604 	goto no_topo;
4605     gaiatopo_reset_last_error_msg (accessor);
4606 
4607 /* checking the input GeoTable */
4608     if (!check_input_geo_table
4609 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &family,
4610 	 &dims))
4611 	goto no_input;
4612     if (!check_matching_srid_dims (accessor, srid, dims))
4613 	goto invalid_geom;
4614 
4615     start_topo_savepoint (sqlite, cache);
4616     ret =
4617 	gaiaTopoGeo_FromGeoTable (accessor, db_prefix, xtable, xcolumn,
4618 				  tolerance, line_max_points, max_length);
4619     if (!ret)
4620 	rollback_topo_savepoint (sqlite, cache);
4621     else
4622 	release_topo_savepoint (sqlite, cache);
4623     free (xtable);
4624     free (xcolumn);
4625     if (!ret)
4626       {
4627 	  msg = gaiaGetRtTopoErrorMsg (cache);
4628 	  gaiatopo_set_last_error_msg (accessor, msg);
4629 	  sqlite3_result_error (context, msg, -1);
4630 	  return;
4631       }
4632     sqlite3_result_int (context, 1);
4633     return;
4634 
4635   no_topo:
4636     if (xtable != NULL)
4637 	free (xtable);
4638     if (xcolumn != NULL)
4639 	free (xcolumn);
4640     msg = "SQL/MM Spatial exception - invalid topology name.";
4641     gaiatopo_set_last_error_msg (accessor, msg);
4642     sqlite3_result_error (context, msg, -1);
4643     return;
4644 
4645   no_input:
4646     if (xtable != NULL)
4647 	free (xtable);
4648     if (xcolumn != NULL)
4649 	free (xcolumn);
4650     msg = "SQL/MM Spatial exception - invalid input GeoTable.";
4651     gaiatopo_set_last_error_msg (accessor, msg);
4652     sqlite3_result_error (context, msg, -1);
4653     return;
4654 
4655   null_arg:
4656     if (xtable != NULL)
4657 	free (xtable);
4658     if (xcolumn != NULL)
4659 	free (xcolumn);
4660     msg = "SQL/MM Spatial exception - null argument.";
4661     gaiatopo_set_last_error_msg (accessor, msg);
4662     sqlite3_result_error (context, msg, -1);
4663     return;
4664 
4665   invalid_arg:
4666     if (xtable != NULL)
4667 	free (xtable);
4668     if (xcolumn != NULL)
4669 	free (xcolumn);
4670     msg = "SQL/MM Spatial exception - invalid argument.";
4671     gaiatopo_set_last_error_msg (accessor, msg);
4672     sqlite3_result_error (context, msg, -1);
4673     return;
4674 
4675   invalid_geom:
4676     if (xtable != NULL)
4677 	free (xtable);
4678     if (xcolumn != NULL)
4679 	free (xcolumn);
4680     msg =
4681 	"SQL/MM Spatial exception - invalid GeoTable (mismatching SRID or dimensions).";
4682     gaiatopo_set_last_error_msg (accessor, msg);
4683     sqlite3_result_error (context, msg, -1);
4684     return;
4685 
4686   negative_tolerance:
4687     if (xtable != NULL)
4688 	free (xtable);
4689     if (xcolumn != NULL)
4690 	free (xcolumn);
4691     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
4692     gaiatopo_set_last_error_msg (accessor, msg);
4693     sqlite3_result_error (context, msg, -1);
4694     return;
4695 
4696   illegal_max_points:
4697     if (xtable != NULL)
4698 	free (xtable);
4699     if (xcolumn != NULL)
4700 	free (xcolumn);
4701     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
4702     gaiatopo_set_last_error_msg (accessor, msg);
4703     sqlite3_result_error (context, msg, -1);
4704     return;
4705 
4706   nonpositive_max_length:
4707     if (xtable != NULL)
4708 	free (xtable);
4709     if (xcolumn != NULL)
4710 	free (xcolumn);
4711     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
4712     gaiatopo_set_last_error_msg (accessor, msg);
4713     sqlite3_result_error (context, msg, -1);
4714     return;
4715 }
4716 
4717 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_FromGeoTableNoFace(const void * xcontext,int argc,const void * xargv)4718 fnctaux_TopoGeo_FromGeoTableNoFace (const void *xcontext, int argc,
4719 				    const void *xargv)
4720 {
4721 /* SQL function:
4722 / TopoGeo_FromGeoTableNoFace ( text topology-name, text db-prefix, text table,
4723 /                        text column )
4724 / TopoGeo_FromGeoTableNoFace ( text topology-name, text db-prefix, text table,
4725 /                        text column, int line_max_points )
4726 / TopoGeo_FromGeoTableNoFace ( text topology-name, text db-prefix, text table,
4727 /                        text column, int line_max_points, double max_length )
4728 / TopoGeo_FromGeoTableNoFace ( text topology-name, text db-prefix, text table,
4729 /                        text column, int line_max_points, double max_length,
4730 /                        double tolerance )
4731 /
4732 / returns: 1 on success
4733 / raises an exception on failure
4734 */
4735     const char *msg;
4736     int ret;
4737     const char *topo_name;
4738     const char *db_prefix;
4739     const char *table;
4740     const char *column;
4741     char *xtable = NULL;
4742     char *xcolumn = NULL;
4743     int srid;
4744     int family;
4745     int dims;
4746     int line_max_points = -1;
4747     double max_length = -1.0;
4748     double tolerance = -1;
4749     struct gaia_topology *topo;
4750     GaiaTopologyAccessorPtr accessor = NULL;
4751     sqlite3_context *context = (sqlite3_context *) xcontext;
4752     sqlite3_value **argv = (sqlite3_value **) xargv;
4753     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4754     struct splite_internal_cache *cache = sqlite3_user_data (context);
4755     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4756     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4757 	goto null_arg;
4758     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4759 	topo_name = (const char *) sqlite3_value_text (argv[0]);
4760     else
4761 	goto invalid_arg;
4762     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4763 	db_prefix = "main";
4764     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
4765 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
4766     else
4767 	goto invalid_arg;
4768     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
4769 	goto null_arg;
4770     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
4771 	table = (const char *) sqlite3_value_text (argv[2]);
4772     else
4773 	goto invalid_arg;
4774     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
4775 	column = NULL;
4776     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
4777 	column = (const char *) sqlite3_value_text (argv[3]);
4778     else
4779 	goto invalid_arg;
4780     if (argc >= 5)
4781       {
4782 	  if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
4783 	      ;
4784 	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
4785 	    {
4786 		line_max_points = sqlite3_value_int (argv[4]);
4787 		if (line_max_points < 2)
4788 		    goto illegal_max_points;
4789 	    }
4790 	  else
4791 	      goto invalid_arg;
4792       }
4793     if (argc >= 6)
4794       {
4795 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
4796 	      ;
4797 	  else
4798 	    {
4799 		if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
4800 		  {
4801 		      int max = sqlite3_value_int (argv[5]);
4802 		      max_length = max;
4803 		  }
4804 		else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
4805 		    max_length = sqlite3_value_double (argv[5]);
4806 		else
4807 		    goto invalid_arg;
4808 		if (max_length <= 0.0)
4809 		    goto nonpositive_max_length;
4810 	    }
4811       }
4812     if (argc >= 7)
4813       {
4814 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
4815 	      goto null_arg;
4816 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
4817 	    {
4818 		int t = sqlite3_value_int (argv[6]);
4819 		tolerance = t;
4820 	    }
4821 	  else if (sqlite3_value_type (argv[6]) == SQLITE_FLOAT)
4822 	      tolerance = sqlite3_value_double (argv[6]);
4823 	  else
4824 	      goto invalid_arg;
4825 	  if (tolerance < 0.0)
4826 	      goto negative_tolerance;
4827       }
4828 
4829 /* attempting to get a Topology Accessor */
4830     accessor = gaiaGetTopology (sqlite, cache, topo_name);
4831     if (accessor == NULL)
4832 	goto no_topo;
4833     topo = (struct gaia_topology *) accessor;
4834     gaiatopo_reset_last_error_msg (accessor);
4835 
4836 /* checking the input GeoTable */
4837     if (!check_input_geo_table
4838 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &family,
4839 	 &dims))
4840 	goto no_input;
4841     if (!check_matching_srid_dims (accessor, srid, dims))
4842 	goto invalid_geom;
4843 
4844     start_topo_savepoint (sqlite, cache);
4845 
4846 /* removing any existing Face except the Universal one */
4847     if (kill_all_existing_faces (sqlite, topo->topology_name) == 0)
4848       {
4849 	  msg = "TopoGeo_FromGeoTableNoFace: unable to remove existing Faces";
4850 	  gaiatopo_set_last_error_msg (accessor, msg);
4851 	  sqlite3_result_error (context, msg, -1);
4852 	  return;
4853       }
4854 
4855     ret =
4856 	gaiaTopoGeo_FromGeoTableNoFace (accessor, db_prefix, xtable, xcolumn,
4857 					tolerance, line_max_points, max_length);
4858     if (!ret)
4859 	rollback_topo_savepoint (sqlite, cache);
4860     else
4861 	release_topo_savepoint (sqlite, cache);
4862     free (xtable);
4863     free (xcolumn);
4864     if (!ret)
4865       {
4866 	  msg = gaiaGetRtTopoErrorMsg (cache);
4867 	  gaiatopo_set_last_error_msg (accessor, msg);
4868 	  sqlite3_result_error (context, msg, -1);
4869 	  return;
4870       }
4871     sqlite3_result_int (context, 1);
4872     return;
4873 
4874   no_topo:
4875     if (xtable != NULL)
4876 	free (xtable);
4877     if (xcolumn != NULL)
4878 	free (xcolumn);
4879     msg = "SQL/MM Spatial exception - invalid topology name.";
4880     gaiatopo_set_last_error_msg (accessor, msg);
4881     sqlite3_result_error (context, msg, -1);
4882     return;
4883 
4884   no_input:
4885     if (xtable != NULL)
4886 	free (xtable);
4887     if (xcolumn != NULL)
4888 	free (xcolumn);
4889     msg = "SQL/MM Spatial exception - invalid input GeoTable.";
4890     gaiatopo_set_last_error_msg (accessor, msg);
4891     sqlite3_result_error (context, msg, -1);
4892     return;
4893 
4894   null_arg:
4895     if (xtable != NULL)
4896 	free (xtable);
4897     if (xcolumn != NULL)
4898 	free (xcolumn);
4899     msg = "SQL/MM Spatial exception - null argument.";
4900     gaiatopo_set_last_error_msg (accessor, msg);
4901     sqlite3_result_error (context, msg, -1);
4902     return;
4903 
4904   invalid_arg:
4905     if (xtable != NULL)
4906 	free (xtable);
4907     if (xcolumn != NULL)
4908 	free (xcolumn);
4909     msg = "SQL/MM Spatial exception - invalid argument.";
4910     gaiatopo_set_last_error_msg (accessor, msg);
4911     sqlite3_result_error (context, msg, -1);
4912     return;
4913 
4914   invalid_geom:
4915     if (xtable != NULL)
4916 	free (xtable);
4917     if (xcolumn != NULL)
4918 	free (xcolumn);
4919     msg =
4920 	"SQL/MM Spatial exception - invalid GeoTable (mismatching SRID or dimensions).";
4921     gaiatopo_set_last_error_msg (accessor, msg);
4922     sqlite3_result_error (context, msg, -1);
4923     return;
4924 
4925   negative_tolerance:
4926     if (xtable != NULL)
4927 	free (xtable);
4928     if (xcolumn != NULL)
4929 	free (xcolumn);
4930     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
4931     gaiatopo_set_last_error_msg (accessor, msg);
4932     sqlite3_result_error (context, msg, -1);
4933     return;
4934 
4935   illegal_max_points:
4936     if (xtable != NULL)
4937 	free (xtable);
4938     if (xcolumn != NULL)
4939 	free (xcolumn);
4940     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
4941     gaiatopo_set_last_error_msg (accessor, msg);
4942     sqlite3_result_error (context, msg, -1);
4943     return;
4944 
4945   nonpositive_max_length:
4946     if (xtable != NULL)
4947 	free (xtable);
4948     if (xcolumn != NULL)
4949 	free (xcolumn);
4950     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
4951     gaiatopo_set_last_error_msg (accessor, msg);
4952     sqlite3_result_error (context, msg, -1);
4953     return;
4954 }
4955 
4956 static int
create_dustbin_table(sqlite3 * sqlite,const char * db_prefix,const char * table,const char * dustbin_table)4957 create_dustbin_table (sqlite3 * sqlite, const char *db_prefix,
4958 		      const char *table, const char *dustbin_table)
4959 {
4960 /* attempting to create a dustbin table */
4961     char *xprefix;
4962     char *xtable;
4963     char *sql;
4964     char *prev_sql;
4965     int ret;
4966     char *err_msg = NULL;
4967     int i;
4968     char **results;
4969     int rows;
4970     int columns;
4971     const char *value;
4972     int error = 0;
4973     struct pk_struct *pk_dictionary = NULL;
4974     struct pk_item *pI;
4975 
4976 /* checking if the target table already exists */
4977     xprefix = gaiaDoubleQuotedSql (db_prefix);
4978     sql =
4979 	sqlite3_mprintf
4980 	("SELECT Count(*) FROM \"%s\".sqlite_master WHERE Lower(name) = Lower(%Q)",
4981 	 xprefix, dustbin_table);
4982     free (xprefix);
4983     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
4984     sqlite3_free (sql);
4985     if (ret != SQLITE_OK)
4986 	return 0;
4987     if (rows < 1)
4988 	;
4989     else
4990       {
4991 	  for (i = 1; i <= rows; i++)
4992 	    {
4993 		value = results[(i * columns) + 0];
4994 		if (atoi (value) != 0)
4995 		    error = 1;
4996 	    }
4997       }
4998     sqlite3_free_table (results);
4999     if (error)
5000       {
5001 	  spatialite_e
5002 	      ("TopoGeo_FromGeoTableExt: dustbin-table \"%s\" already exists\n",
5003 	       dustbin_table);
5004 	  return 0;
5005       }
5006 
5007 /* identifying all Primary Key columns */
5008     xprefix = gaiaDoubleQuotedSql (db_prefix);
5009     xtable = gaiaDoubleQuotedSql (table);
5010     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, xtable);
5011     free (xprefix);
5012     free (xtable);
5013     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
5014     sqlite3_free (sql);
5015     if (ret != SQLITE_OK)
5016 	return 0;
5017     pk_dictionary = create_pk_dictionary ();
5018     if (rows < 1)
5019 	;
5020     else
5021       {
5022 	  for (i = 1; i <= rows; i++)
5023 	    {
5024 		const char *name = results[(i * columns) + 1];
5025 		const char *type = results[(i * columns) + 2];
5026 		int notnull = atoi (results[(i * columns) + 3]);
5027 		int pk = atoi (results[(i * columns) + 5]);
5028 		if (pk > 0)
5029 		    add_pk_column (pk_dictionary, name, type, notnull, pk);
5030 	    }
5031       }
5032     sqlite3_free_table (results);
5033     if (pk_dictionary->count <= 0)
5034       {
5035 	  free_pk_dictionary (pk_dictionary);
5036 	  spatialite_e
5037 	      ("TopoGeo_FromGeoTableExt: the input table \"%s\" has no Primary Key\n",
5038 	       table);
5039 	  return 0;
5040       }
5041 
5042 /* going to create the dustbin table */
5043     xprefix = gaiaDoubleQuotedSql (db_prefix);
5044     xtable = gaiaDoubleQuotedSql (dustbin_table);
5045     sql = sqlite3_mprintf ("CREATE TABLE \"%s\".\"%s\" (\n", xprefix, xtable);
5046     free (xprefix);
5047     free (xtable);
5048     prev_sql = sql;
5049     pI = pk_dictionary->first;
5050     while (pI != NULL)
5051       {
5052 	  char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5053 	  if (pI->notnull)
5054 	      sql =
5055 		  sqlite3_mprintf ("%s\t\"%s\" %s NOT NULL,\n", prev_sql,
5056 				   xcolumn, pI->type);
5057 	  else
5058 	      sql =
5059 		  sqlite3_mprintf ("%s\t\"%s\" %s,\n", prev_sql, xcolumn,
5060 				   pI->type);
5061 	  free (xcolumn);
5062 	  sqlite3_free (prev_sql);
5063 	  prev_sql = sql;
5064 	  pI = pI->next;
5065       }
5066     xprefix = sqlite3_mprintf ("pk_%s", dustbin_table);
5067     xtable = gaiaDoubleQuotedSql (xprefix);
5068     sqlite3_free (xprefix);
5069     sql =
5070 	sqlite3_mprintf ("%s\tmessage TEXT,\n\ttolerance DOUBLE NOT NULL,\n"
5071 			 "\tfailing_geometry BLOB\n,\tCONSTRAINT \"%s\" PRIMARY KEY (",
5072 			 prev_sql, xtable);
5073     sqlite3_free (prev_sql);
5074     free (xtable);
5075     prev_sql = sql;
5076     for (i = 1; i <= pk_dictionary->count; i++)
5077       {
5078 	  pI = pk_dictionary->first;
5079 	  while (pI != NULL)
5080 	    {
5081 		if (pI->pk == i)
5082 		  {
5083 		      char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5084 		      if (i == 1)
5085 			  sql = sqlite3_mprintf ("%s\"%s\"", prev_sql, xcolumn);
5086 		      else
5087 			  sql =
5088 			      sqlite3_mprintf ("%s, \"%s\"", prev_sql, xcolumn);
5089 		      sqlite3_free (prev_sql);
5090 		      free (xcolumn);
5091 		      prev_sql = sql;
5092 		  }
5093 		pI = pI->next;
5094 	    }
5095       }
5096     sql = sqlite3_mprintf ("%s))", prev_sql);
5097     sqlite3_free (prev_sql);
5098     free_pk_dictionary (pk_dictionary);
5099 
5100     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
5101     sqlite3_free (sql);
5102     if (ret != SQLITE_OK)
5103       {
5104 	  spatialite_e
5105 	      ("TopoGeo_FromGeoTableExt: unable to create dustbin-table \"%s\": %s\n",
5106 	       dustbin_table, err_msg);
5107 	  sqlite3_free (err_msg);
5108 	  return 0;
5109       }
5110     return 1;
5111 }
5112 
5113 static int
create_dustbin_view(sqlite3 * sqlite,const char * db_prefix,const char * table,const char * column,const char * dustbin_table,const char * dustbin_view,char ** sql_in,char ** sql_out,char ** sql_in2)5114 create_dustbin_view (sqlite3 * sqlite, const char *db_prefix, const char *table,
5115 		     const char *column, const char *dustbin_table,
5116 		     const char *dustbin_view, char **sql_in, char **sql_out,
5117 		     char **sql_in2)
5118 {
5119 /* attempting to create a dustbin view */
5120     char *xprefix;
5121     char *xtable;
5122     char *xcolumn;
5123     char *sql;
5124     char *prev_sql;
5125     char *sql2;
5126     int ret;
5127     char *err_msg = NULL;
5128     int i;
5129     char **results;
5130     int rows;
5131     int columns;
5132     const char *value;
5133     int error = 0;
5134     struct pk_struct *pk_dictionary = NULL;
5135     struct pk_item *pI;
5136     int first;
5137 
5138     *sql_in = NULL;
5139     *sql_out = NULL;
5140     *sql_in2 = NULL;
5141 /* checking if the target view already exists */
5142     xprefix = gaiaDoubleQuotedSql (db_prefix);
5143     sql =
5144 	sqlite3_mprintf
5145 	("SELECT Count(*) FROM \"%s\".sqlite_master WHERE Lower(name) = Lower(%Q)",
5146 	 xprefix, dustbin_view);
5147     free (xprefix);
5148     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
5149     sqlite3_free (sql);
5150     if (ret != SQLITE_OK)
5151 	return 0;
5152     if (rows < 1)
5153 	;
5154     else
5155       {
5156 	  for (i = 1; i <= rows; i++)
5157 	    {
5158 		value = results[(i * columns) + 0];
5159 		if (atoi (value) != 0)
5160 		    error = 1;
5161 	    }
5162       }
5163     sqlite3_free_table (results);
5164     if (error)
5165 	return 0;
5166 
5167 /* identifying all main table's columns */
5168     xprefix = gaiaDoubleQuotedSql (db_prefix);
5169     xtable = gaiaDoubleQuotedSql (table);
5170     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, xtable);
5171     free (xprefix);
5172     free (xtable);
5173     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
5174     sqlite3_free (sql);
5175     if (ret != SQLITE_OK)
5176 	return 0;
5177     pk_dictionary = create_pk_dictionary ();
5178     if (rows < 1)
5179 	;
5180     else
5181       {
5182 	  for (i = 1; i <= rows; i++)
5183 	    {
5184 		const char *name = results[(i * columns) + 1];
5185 		const char *type = results[(i * columns) + 2];
5186 		int notnull = atoi (results[(i * columns) + 3]);
5187 		int pk = atoi (results[(i * columns) + 5]);
5188 		add_pk_column (pk_dictionary, name, type, notnull, pk);
5189 	    }
5190       }
5191     sqlite3_free_table (results);
5192     if (pk_dictionary->count <= 0)
5193       {
5194 	  free_pk_dictionary (pk_dictionary);
5195 	  spatialite_e
5196 	      ("TopoGeo_FromGeoTableExt: unable to retrieve \"%s\" columns\n",
5197 	       table);
5198 	  return 0;
5199       }
5200 
5201 /* going to create the dustbin view */
5202     xprefix = gaiaDoubleQuotedSql (db_prefix);
5203     xtable = gaiaDoubleQuotedSql (dustbin_view);
5204     sql = sqlite3_mprintf ("CREATE VIEW \"%s\".\"%s\" AS\n"
5205 			   "SELECT a.ROWID AS rowid", xprefix, xtable);
5206     free (xprefix);
5207     free (xtable);
5208     prev_sql = sql;
5209     pI = pk_dictionary->first;
5210     while (pI != NULL)
5211       {
5212 	  char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5213 	  sql =
5214 	      sqlite3_mprintf ("%s, a.\"%s\" AS \"%s\"", prev_sql, xcolumn,
5215 			       xcolumn);
5216 	  free (xcolumn);
5217 	  sqlite3_free (prev_sql);
5218 	  prev_sql = sql;
5219 	  pI = pI->next;
5220       }
5221     xtable = gaiaDoubleQuotedSql (table);
5222     xprefix = gaiaDoubleQuotedSql (dustbin_table);
5223     sql =
5224 	sqlite3_mprintf
5225 	("%s, b.message AS message, b.tolerance AS tolerance, "
5226 	 "b.failing_geometry AS failing_geometry "
5227 	 "FROM \"%s\" AS a, \"%s\" AS b\nWHERE ", prev_sql, xtable, xprefix);
5228     sqlite3_free (prev_sql);
5229     free (xtable);
5230     free (xprefix);
5231     prev_sql = sql;
5232     pI = pk_dictionary->first;
5233     first = 1;
5234     while (pI != NULL)
5235       {
5236 	  if (pI->pk > 0)
5237 	    {
5238 		char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5239 		if (first)
5240 		    sql =
5241 			sqlite3_mprintf ("%sa.\"%s\" = b.\"%s\"", prev_sql,
5242 					 xcolumn, xcolumn);
5243 		else
5244 		    sql =
5245 			sqlite3_mprintf ("%s AND a.\"%s\" = b.\"%s\"", prev_sql,
5246 					 xcolumn, xcolumn);
5247 		first = 0;
5248 		sqlite3_free (prev_sql);
5249 		free (xcolumn);
5250 		prev_sql = sql;
5251 	    }
5252 	  pI = pI->next;
5253       }
5254     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
5255     sqlite3_free (sql);
5256     if (ret != SQLITE_OK)
5257       {
5258 	  spatialite_e
5259 	      ("TopoGeo_FromGeoTableExt: unable to create dustbin-view \"%s\": %s\n",
5260 	       dustbin_table, err_msg);
5261 	  sqlite3_free (err_msg);
5262 	  free_pk_dictionary (pk_dictionary);
5263 	  return 0;
5264       }
5265 
5266 /* registering the Spatial View */
5267     xprefix = gaiaDoubleQuotedSql (db_prefix);
5268     sql =
5269 	sqlite3_mprintf
5270 	("INSERT INTO \"%s\".views_geometry_columns (view_name, "
5271 	 "view_geometry, view_rowid, f_table_name, f_geometry_column, read_only) "
5272 	 "VALUES (%Q, %Q, 'rowid',  %Q, %Q, 1)", xprefix, dustbin_view, column,
5273 	 table, column);
5274     free (xprefix);
5275     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
5276     sqlite3_free (sql);
5277     if (ret != SQLITE_OK)
5278       {
5279 	  spatialite_e
5280 	      ("TopoGeo_FromGeoTableExt: unable to register the dustbin-view \"%s\": %s\n",
5281 	       dustbin_table, err_msg);
5282 	  sqlite3_free (err_msg);
5283 	  free_pk_dictionary (pk_dictionary);
5284 	  return 0;
5285       }
5286 
5287 /* constructing the input SQL statement */
5288     sql = sqlite3_mprintf ("SELECT ROWID");
5289     prev_sql = sql;
5290     pI = pk_dictionary->first;
5291     while (pI != NULL)
5292       {
5293 	  if (pI->pk > 0)
5294 	    {
5295 		char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5296 		sql = sqlite3_mprintf ("%s, \"%s\"", prev_sql, xcolumn);
5297 		sqlite3_free (prev_sql);
5298 		free (xcolumn);
5299 		prev_sql = sql;
5300 	    }
5301 	  pI = pI->next;
5302       }
5303     xcolumn = gaiaDoubleQuotedSql (column);
5304     xprefix = gaiaDoubleQuotedSql (db_prefix);
5305     xtable = gaiaDoubleQuotedSql (table);
5306     sql =
5307 	sqlite3_mprintf ("%s, \"%s\" FROM \"%s\".\"%s\" "
5308 			 "WHERE ROWID > ? ORDER BY ROWID", prev_sql, xcolumn,
5309 			 xprefix, xtable);
5310     sql2 =
5311 	sqlite3_mprintf ("%s, \"%s\" FROM \"%s\".\"%s\" WHERE ROWID = ?",
5312 			 prev_sql, xcolumn, xprefix, xtable);
5313     free (xcolumn);
5314     free (xprefix);
5315     free (xtable);
5316     sqlite3_free (prev_sql);
5317     *sql_in = sql;
5318     *sql_in2 = sql2;
5319 
5320 /* constructing the output SQL statement */
5321     xprefix = gaiaDoubleQuotedSql (db_prefix);
5322     xtable = gaiaDoubleQuotedSql (dustbin_table);
5323     sql = sqlite3_mprintf ("INSERT INTO \"%s\".\"%s\" (", xprefix, xtable);
5324     prev_sql = sql;
5325     free (xprefix);
5326     free (xtable);
5327     pI = pk_dictionary->first;
5328     first = 1;
5329     while (pI != NULL)
5330       {
5331 	  if (pI->pk > 0)
5332 	    {
5333 		char *xcolumn = gaiaDoubleQuotedSql (pI->name);
5334 		if (first)
5335 		    sql = sqlite3_mprintf ("%s\"%s\"", prev_sql, xcolumn);
5336 		else
5337 		    sql = sqlite3_mprintf ("%s, \"%s\"", prev_sql, xcolumn);
5338 		first = 0;
5339 		sqlite3_free (prev_sql);
5340 		free (xcolumn);
5341 		prev_sql = sql;
5342 	    }
5343 	  pI = pI->next;
5344       }
5345     sql =
5346 	sqlite3_mprintf ("%s, message, tolerance, failing_geometry) VALUES (",
5347 			 prev_sql);
5348     sqlite3_free (prev_sql);
5349     prev_sql = sql;
5350     pI = pk_dictionary->first;
5351     first = 1;
5352     while (pI != NULL)
5353       {
5354 	  if (pI->pk > 0)
5355 	    {
5356 		if (first)
5357 		    sql = sqlite3_mprintf ("%s?", prev_sql);
5358 		else
5359 		    sql = sqlite3_mprintf ("%s, ?", prev_sql);
5360 		first = 0;
5361 		sqlite3_free (prev_sql);
5362 		prev_sql = sql;
5363 	    }
5364 	  pI = pI->next;
5365       }
5366     sql = sqlite3_mprintf ("%s, ?, ?, ?)", prev_sql);
5367     sqlite3_free (prev_sql);
5368     *sql_out = sql;
5369 
5370     free_pk_dictionary (pk_dictionary);
5371     return 1;
5372 }
5373 
5374 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_FromGeoTableExt(const void * xcontext,int argc,const void * xargv)5375 fnctaux_TopoGeo_FromGeoTableExt (const void *xcontext, int argc,
5376 				 const void *xargv)
5377 {
5378 /* SQL function:
5379 / TopoGeo_FromGeoTableExt ( text topology-name, text db-prefix, text table,
5380 /                           text column, text dustbin-table, text dustbin-view )
5381 / TopoGeo_FromGeoTableExt ( text topology-name, text db-prefix, text table,
5382 /                           text column, text dustbin-table, text dustbin-view,
5383 /                           int line_max_points )
5384 / TopoGeo_FromGeoTableExt ( text topology-name, text db-prefix, text table,
5385 /                           text column, text dustbin-table, text dustbin-view,
5386 /                           int line_max_points, double max_length )
5387 / TopoGeo_FromGeoTableExt ( text topology-name, text db-prefix, text table,
5388 /                           text column, text dustbin-table, text dustbin-view,
5389 /                           int line_max_points, double max_length ,
5390 /                           double tolerance )
5391 /
5392 / returns: 1 on success
5393 / raises an exception on failure
5394 */
5395     const char *msg;
5396     int ret;
5397     const char *topo_name;
5398     const char *db_prefix;
5399     const char *table;
5400     const char *column;
5401     char *xtable = NULL;
5402     char *xcolumn = NULL;
5403     int srid;
5404     int family;
5405     int dims;
5406     const char *dustbin_table;
5407     const char *dustbin_view;
5408     int line_max_points = -1;
5409     double max_length = -1.0;
5410     double tolerance = -1;
5411     char *sql_in = NULL;
5412     char *sql_out = NULL;
5413     char *sql_in2 = NULL;
5414     GaiaTopologyAccessorPtr accessor = NULL;
5415     sqlite3_context *context = (sqlite3_context *) xcontext;
5416     sqlite3_value **argv = (sqlite3_value **) xargv;
5417     sqlite3 *sqlite = sqlite3_context_db_handle (context);
5418     struct splite_internal_cache *cache = sqlite3_user_data (context);
5419     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
5420     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
5421 	goto null_arg;
5422     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
5423 	topo_name = (const char *) sqlite3_value_text (argv[0]);
5424     else
5425 	goto invalid_arg;
5426     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
5427 	db_prefix = "main";
5428     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
5429 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
5430     else
5431 	goto invalid_arg;
5432     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
5433 	goto null_arg;
5434     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
5435 	table = (const char *) sqlite3_value_text (argv[2]);
5436     else
5437 	goto invalid_arg;
5438     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
5439 	column = NULL;
5440     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
5441 	column = (const char *) sqlite3_value_text (argv[3]);
5442     else
5443 	goto invalid_arg;
5444     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
5445 	goto null_arg;
5446     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
5447 	dustbin_table = (const char *) sqlite3_value_text (argv[4]);
5448     else
5449 	goto invalid_arg;
5450     if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
5451 	goto null_arg;
5452     else if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
5453 	dustbin_view = (const char *) sqlite3_value_text (argv[5]);
5454     else
5455 	goto invalid_arg;
5456     if (argc >= 7)
5457       {
5458 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
5459 	      ;
5460 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
5461 	    {
5462 		line_max_points = sqlite3_value_int (argv[6]);
5463 		if (line_max_points < 2)
5464 		    goto illegal_max_points;
5465 	    }
5466 	  else
5467 	      goto invalid_arg;
5468       }
5469     if (argc >= 8)
5470       {
5471 	  if (sqlite3_value_type (argv[7]) == SQLITE_NULL)
5472 	      ;
5473 	  else
5474 	    {
5475 		if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
5476 		  {
5477 		      int max = sqlite3_value_int (argv[7]);
5478 		      max_length = max;
5479 		  }
5480 		else if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
5481 		    max_length = sqlite3_value_double (argv[7]);
5482 		else
5483 		    goto invalid_arg;
5484 		if (max_length <= 0.0)
5485 		    goto nonpositive_max_length;
5486 	    }
5487       }
5488     if (argc >= 9)
5489       {
5490 	  if (sqlite3_value_type (argv[8]) == SQLITE_NULL)
5491 	      goto null_arg;
5492 	  else if (sqlite3_value_type (argv[8]) == SQLITE_INTEGER)
5493 	    {
5494 		int t = sqlite3_value_int (argv[8]);
5495 		tolerance = t;
5496 	    }
5497 	  else if (sqlite3_value_type (argv[8]) == SQLITE_FLOAT)
5498 	      tolerance = sqlite3_value_double (argv[8]);
5499 	  else
5500 	      goto invalid_arg;
5501 	  if (tolerance < 0.0)
5502 	      goto negative_tolerance;
5503       }
5504 
5505 /* attempting to get a Topology Accessor */
5506     accessor = gaiaGetTopology (sqlite, cache, topo_name);
5507     if (accessor == NULL)
5508 	goto no_topo;
5509     gaiatopo_reset_last_error_msg (accessor);
5510 
5511 /* checking the input GeoTable */
5512     if (!check_input_geo_table
5513 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &family,
5514 	 &dims))
5515 	goto no_input;
5516     if (!check_matching_srid_dims (accessor, srid, dims))
5517 	goto invalid_geom;
5518 
5519 /* attempting to create the dustbin table and view */
5520     start_topo_savepoint (sqlite, cache);
5521     if (!create_dustbin_table (sqlite, db_prefix, xtable, dustbin_table))
5522       {
5523 	  rollback_topo_savepoint (sqlite, cache);
5524 	  goto no_dustbin_table;
5525       }
5526     if (!create_dustbin_view
5527 	(sqlite, db_prefix, xtable, xcolumn, dustbin_table, dustbin_view,
5528 	 &sql_in, &sql_out, &sql_in2))
5529       {
5530 	  rollback_topo_savepoint (sqlite, cache);
5531 	  goto no_dustbin_view;
5532       }
5533     release_topo_savepoint (sqlite, cache);
5534 
5535     ret =
5536 	gaiaTopoGeo_FromGeoTableExtended (accessor, sql_in, sql_out, sql_in2,
5537 					  tolerance, line_max_points,
5538 					  max_length);
5539     free (xtable);
5540     free (xcolumn);
5541     sqlite3_free (sql_in);
5542     sqlite3_free (sql_out);
5543     sqlite3_free (sql_in2);
5544     sqlite3_result_int (context, ret);
5545     return;
5546 
5547   no_topo:
5548     if (xtable != NULL)
5549 	free (xtable);
5550     if (xcolumn != NULL)
5551 	free (xcolumn);
5552     if (sql_in != NULL)
5553 	sqlite3_free (sql_in);
5554     if (sql_out != NULL)
5555 	sqlite3_free (sql_out);
5556     if (sql_in2 != NULL)
5557 	sqlite3_free (sql_in2);
5558     msg = "SQL/MM Spatial exception - invalid topology name.";
5559     gaiatopo_set_last_error_msg (accessor, msg);
5560     sqlite3_result_error (context, msg, -1);
5561     return;
5562 
5563   no_input:
5564     if (xtable != NULL)
5565 	free (xtable);
5566     if (xcolumn != NULL)
5567 	free (xcolumn);
5568     if (sql_in != NULL)
5569 	sqlite3_free (sql_in);
5570     if (sql_out != NULL)
5571 	sqlite3_free (sql_out);
5572     if (sql_in2 != NULL)
5573 	sqlite3_free (sql_in2);
5574     msg = "SQL/MM Spatial exception - invalid input GeoTable.";
5575     gaiatopo_set_last_error_msg (accessor, msg);
5576     sqlite3_result_error (context, msg, -1);
5577     return;
5578 
5579   null_arg:
5580     if (xtable != NULL)
5581 	free (xtable);
5582     if (xcolumn != NULL)
5583 	free (xcolumn);
5584     if (sql_in != NULL)
5585 	sqlite3_free (sql_in);
5586     if (sql_out != NULL)
5587 	sqlite3_free (sql_out);
5588     if (sql_in2 != NULL)
5589 	sqlite3_free (sql_in2);
5590     msg = "SQL/MM Spatial exception - null argument.";
5591     gaiatopo_set_last_error_msg (accessor, msg);
5592     sqlite3_result_error (context, msg, -1);
5593     return;
5594 
5595   invalid_arg:
5596     if (xtable != NULL)
5597 	free (xtable);
5598     if (xcolumn != NULL)
5599 	free (xcolumn);
5600     if (sql_in != NULL)
5601 	sqlite3_free (sql_in);
5602     if (sql_out != NULL)
5603 	sqlite3_free (sql_out);
5604     if (sql_in2 != NULL)
5605 	sqlite3_free (sql_in2);
5606     msg = "SQL/MM Spatial exception - invalid argument.";
5607     gaiatopo_set_last_error_msg (accessor, msg);
5608     sqlite3_result_error (context, msg, -1);
5609     return;
5610 
5611   invalid_geom:
5612     if (xtable != NULL)
5613 	free (xtable);
5614     if (xcolumn != NULL)
5615 	free (xcolumn);
5616     if (sql_in != NULL)
5617 	sqlite3_free (sql_in);
5618     if (sql_out != NULL)
5619 	sqlite3_free (sql_out);
5620     if (sql_in2 != NULL)
5621 	sqlite3_free (sql_in2);
5622     msg =
5623 	"SQL/MM Spatial exception - invalid GeoTable (mismatching SRID or dimensions).";
5624     gaiatopo_set_last_error_msg (accessor, msg);
5625     sqlite3_result_error (context, msg, -1);
5626     return;
5627 
5628   no_dustbin_table:
5629     if (xtable != NULL)
5630 	free (xtable);
5631     if (xcolumn != NULL)
5632 	free (xcolumn);
5633     if (sql_in != NULL)
5634 	sqlite3_free (sql_in);
5635     if (sql_out != NULL)
5636 	sqlite3_free (sql_out);
5637     msg = "SQL/MM Spatial exception - unable to create the dustbin table.";
5638     gaiatopo_set_last_error_msg (accessor, msg);
5639     sqlite3_result_error (context, msg, -1);
5640     return;
5641 
5642   no_dustbin_view:
5643     if (xtable != NULL)
5644 	free (xtable);
5645     if (xcolumn != NULL)
5646 	free (xcolumn);
5647     if (sql_in != NULL)
5648 	sqlite3_free (sql_in);
5649     if (sql_out != NULL)
5650 	sqlite3_free (sql_out);
5651     if (sql_in2 != NULL)
5652 	sqlite3_free (sql_in2);
5653     msg = "SQL/MM Spatial exception - unable to create the dustbin view.";
5654     gaiatopo_set_last_error_msg (accessor, msg);
5655     sqlite3_result_error (context, msg, -1);
5656     return;
5657 
5658   negative_tolerance:
5659     if (xtable != NULL)
5660 	free (xtable);
5661     if (xcolumn != NULL)
5662 	free (xcolumn);
5663     if (sql_in != NULL)
5664 	sqlite3_free (sql_in);
5665     if (sql_out != NULL)
5666 	sqlite3_free (sql_out);
5667     if (sql_in2 != NULL)
5668 	sqlite3_free (sql_in2);
5669     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
5670     gaiatopo_set_last_error_msg (accessor, msg);
5671     sqlite3_result_error (context, msg, -1);
5672     return;
5673 
5674   illegal_max_points:
5675     if (xtable != NULL)
5676 	free (xtable);
5677     if (xcolumn != NULL)
5678 	free (xcolumn);
5679     if (sql_in != NULL)
5680 	sqlite3_free (sql_in);
5681     if (sql_out != NULL)
5682 	sqlite3_free (sql_out);
5683     if (sql_in2 != NULL)
5684 	sqlite3_free (sql_in2);
5685     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
5686     gaiatopo_set_last_error_msg (accessor, msg);
5687     sqlite3_result_error (context, msg, -1);
5688     return;
5689 
5690   nonpositive_max_length:
5691     if (xtable != NULL)
5692 	free (xtable);
5693     if (xcolumn != NULL)
5694 	free (xcolumn);
5695     if (sql_in != NULL)
5696 	sqlite3_free (sql_in);
5697     if (sql_out != NULL)
5698 	sqlite3_free (sql_out);
5699     if (sql_in2 != NULL)
5700 	sqlite3_free (sql_in2);
5701     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
5702     gaiatopo_set_last_error_msg (accessor, msg);
5703     sqlite3_result_error (context, msg, -1);
5704     return;
5705 }
5706 
5707 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_FromGeoTableNoFaceExt(const void * xcontext,int argc,const void * xargv)5708 fnctaux_TopoGeo_FromGeoTableNoFaceExt (const void *xcontext, int argc,
5709 				       const void *xargv)
5710 {
5711 /* SQL function:
5712 / TopoGeo_FromGeoTableNoFaceExt ( text topology-name, text db-prefix,
5713 /                                 text table, text column, text dustbin-table,
5714 /                                 text dustbin-view )
5715 / TopoGeo_FromGeoTableNoFaceExt ( text topology-name, text db-prefix,
5716 /                                 text table, text column, text dustbin-table,
5717 /                                 text dustbin-view, int line_max_points )
5718 / TopoGeo_FromGeoTableNoFaceExt ( text topology-name, text db-prefix,
5719 /                                 text table, text column, text dustbin-table,
5720 /                                 text dustbin-view, int line_max_points,
5721 /                                 double max_length )
5722 / TopoGeo_FromGeoTableNoFaceExt ( text topology-name, text db-prefix,
5723 /                                 text table, text column, text dustbin-table,
5724 /                                 text dustbin-view, int line_max_points,
5725 /                                 double max_length, double tolerance )
5726 /
5727 / returns: 1 on success
5728 / raises an exception on failure
5729 */
5730     const char *msg;
5731     int ret;
5732     const char *topo_name;
5733     const char *db_prefix;
5734     const char *table;
5735     const char *column;
5736     char *xtable = NULL;
5737     char *xcolumn = NULL;
5738     int srid;
5739     int family;
5740     int dims;
5741     const char *dustbin_table;
5742     const char *dustbin_view;
5743     int line_max_points = -1;
5744     double max_length = -1.0;
5745     double tolerance = -1;
5746     char *sql_in = NULL;
5747     char *sql_out = NULL;
5748     char *sql_in2 = NULL;
5749     struct gaia_topology *topo;
5750     GaiaTopologyAccessorPtr accessor = NULL;
5751     sqlite3_context *context = (sqlite3_context *) xcontext;
5752     sqlite3_value **argv = (sqlite3_value **) xargv;
5753     sqlite3 *sqlite = sqlite3_context_db_handle (context);
5754     struct splite_internal_cache *cache = sqlite3_user_data (context);
5755     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
5756     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
5757 	goto null_arg;
5758     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
5759 	topo_name = (const char *) sqlite3_value_text (argv[0]);
5760     else
5761 	goto invalid_arg;
5762     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
5763 	db_prefix = "main";
5764     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
5765 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
5766     else
5767 	goto invalid_arg;
5768     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
5769 	goto null_arg;
5770     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
5771 	table = (const char *) sqlite3_value_text (argv[2]);
5772     else
5773 	goto invalid_arg;
5774     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
5775 	column = NULL;
5776     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
5777 	column = (const char *) sqlite3_value_text (argv[3]);
5778     else
5779 	goto invalid_arg;
5780     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
5781 	goto null_arg;
5782     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
5783 	dustbin_table = (const char *) sqlite3_value_text (argv[4]);
5784     else
5785 	goto invalid_arg;
5786     if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
5787 	goto null_arg;
5788     else if (sqlite3_value_type (argv[5]) == SQLITE_TEXT)
5789 	dustbin_view = (const char *) sqlite3_value_text (argv[5]);
5790     else
5791 	goto invalid_arg;
5792     if (argc >= 7)
5793       {
5794 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
5795 	      ;
5796 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
5797 	    {
5798 		line_max_points = sqlite3_value_int (argv[6]);
5799 		if (line_max_points < 2)
5800 		    goto illegal_max_points;
5801 	    }
5802 	  else
5803 	      goto invalid_arg;
5804       }
5805     if (argc >= 8)
5806       {
5807 	  if (sqlite3_value_type (argv[7]) == SQLITE_NULL)
5808 	      ;
5809 	  else
5810 	    {
5811 		if (sqlite3_value_type (argv[7]) == SQLITE_INTEGER)
5812 		  {
5813 		      int max = sqlite3_value_int (argv[7]);
5814 		      max_length = max;
5815 		  }
5816 		else if (sqlite3_value_type (argv[7]) == SQLITE_FLOAT)
5817 		    max_length = sqlite3_value_double (argv[7]);
5818 		else
5819 		    goto invalid_arg;
5820 		if (max_length <= 0.0)
5821 		    goto nonpositive_max_length;
5822 	    }
5823       }
5824     if (argc >= 9)
5825       {
5826 	  if (sqlite3_value_type (argv[8]) == SQLITE_NULL)
5827 	      goto null_arg;
5828 	  else if (sqlite3_value_type (argv[8]) == SQLITE_INTEGER)
5829 	    {
5830 		int t = sqlite3_value_int (argv[8]);
5831 		tolerance = t;
5832 	    }
5833 	  else if (sqlite3_value_type (argv[8]) == SQLITE_FLOAT)
5834 	      tolerance = sqlite3_value_double (argv[8]);
5835 	  else
5836 	      goto invalid_arg;
5837 	  if (tolerance < 0.0)
5838 	      goto negative_tolerance;
5839       }
5840 
5841 /* attempting to get a Topology Accessor */
5842     accessor = gaiaGetTopology (sqlite, cache, topo_name);
5843     if (accessor == NULL)
5844 	goto no_topo;
5845     topo = (struct gaia_topology *) accessor;
5846     gaiatopo_reset_last_error_msg (accessor);
5847 
5848 /* checking the input GeoTable */
5849     if (!check_input_geo_table
5850 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &family,
5851 	 &dims))
5852 	goto no_input;
5853     if (!check_matching_srid_dims (accessor, srid, dims))
5854 	goto invalid_geom;
5855 
5856     start_topo_savepoint (sqlite, cache);
5857 
5858 /* removing any existing Face except the Universal one */
5859     if (kill_all_existing_faces (sqlite, topo->topology_name) == 0)
5860       {
5861 	  msg =
5862 	      "TopoGeo_FromGeoTableNoFaceExt: unable to remove existing Faces";
5863 	  gaiatopo_set_last_error_msg (accessor, msg);
5864 	  sqlite3_result_error (context, msg, -1);
5865 	  return;
5866       }
5867 
5868 /* attempting to create the dustbin table and view */
5869     if (!create_dustbin_table (sqlite, db_prefix, xtable, dustbin_table))
5870       {
5871 	  rollback_topo_savepoint (sqlite, cache);
5872 	  goto no_dustbin_table;
5873       }
5874     if (!create_dustbin_view
5875 	(sqlite, db_prefix, xtable, xcolumn, dustbin_table, dustbin_view,
5876 	 &sql_in, &sql_out, &sql_in2))
5877       {
5878 	  rollback_topo_savepoint (sqlite, cache);
5879 	  goto no_dustbin_view;
5880       }
5881     release_topo_savepoint (sqlite, cache);
5882 
5883     ret =
5884 	gaiaTopoGeo_FromGeoTableNoFaceExtended (accessor, sql_in, sql_out,
5885 						sql_in2, tolerance,
5886 						line_max_points, max_length);
5887     free (xtable);
5888     free (xcolumn);
5889     sqlite3_free (sql_in);
5890     sqlite3_free (sql_out);
5891     sqlite3_free (sql_in2);
5892     sqlite3_result_int (context, ret);
5893     return;
5894 
5895   no_topo:
5896     if (xtable != NULL)
5897 	free (xtable);
5898     if (xcolumn != NULL)
5899 	free (xcolumn);
5900     if (sql_in != NULL)
5901 	sqlite3_free (sql_in);
5902     if (sql_out != NULL)
5903 	sqlite3_free (sql_out);
5904     if (sql_in2 != NULL)
5905 	sqlite3_free (sql_in2);
5906     msg = "SQL/MM Spatial exception - invalid topology name.";
5907     gaiatopo_set_last_error_msg (accessor, msg);
5908     sqlite3_result_error (context, msg, -1);
5909     return;
5910 
5911   no_input:
5912     if (xtable != NULL)
5913 	free (xtable);
5914     if (xcolumn != NULL)
5915 	free (xcolumn);
5916     if (sql_in != NULL)
5917 	sqlite3_free (sql_in);
5918     if (sql_out != NULL)
5919 	sqlite3_free (sql_out);
5920     if (sql_in2 != NULL)
5921 	sqlite3_free (sql_in2);
5922     msg = "SQL/MM Spatial exception - invalid input GeoTable.";
5923     gaiatopo_set_last_error_msg (accessor, msg);
5924     sqlite3_result_error (context, msg, -1);
5925     return;
5926 
5927   null_arg:
5928     if (xtable != NULL)
5929 	free (xtable);
5930     if (xcolumn != NULL)
5931 	free (xcolumn);
5932     if (sql_in != NULL)
5933 	sqlite3_free (sql_in);
5934     if (sql_out != NULL)
5935 	sqlite3_free (sql_out);
5936     if (sql_in2 != NULL)
5937 	sqlite3_free (sql_in2);
5938     msg = "SQL/MM Spatial exception - null argument.";
5939     gaiatopo_set_last_error_msg (accessor, msg);
5940     sqlite3_result_error (context, msg, -1);
5941     return;
5942 
5943   invalid_arg:
5944     if (xtable != NULL)
5945 	free (xtable);
5946     if (xcolumn != NULL)
5947 	free (xcolumn);
5948     if (sql_in != NULL)
5949 	sqlite3_free (sql_in);
5950     if (sql_out != NULL)
5951 	sqlite3_free (sql_out);
5952     if (sql_in2 != NULL)
5953 	sqlite3_free (sql_in2);
5954     msg = "SQL/MM Spatial exception - invalid argument.";
5955     gaiatopo_set_last_error_msg (accessor, msg);
5956     sqlite3_result_error (context, msg, -1);
5957     return;
5958 
5959   invalid_geom:
5960     if (xtable != NULL)
5961 	free (xtable);
5962     if (xcolumn != NULL)
5963 	free (xcolumn);
5964     if (sql_in != NULL)
5965 	sqlite3_free (sql_in);
5966     if (sql_out != NULL)
5967 	sqlite3_free (sql_out);
5968     if (sql_in2 != NULL)
5969 	sqlite3_free (sql_in2);
5970     msg =
5971 	"SQL/MM Spatial exception - invalid GeoTable (mismatching SRID or dimensions).";
5972     gaiatopo_set_last_error_msg (accessor, msg);
5973     sqlite3_result_error (context, msg, -1);
5974     return;
5975 
5976   no_dustbin_table:
5977     if (xtable != NULL)
5978 	free (xtable);
5979     if (xcolumn != NULL)
5980 	free (xcolumn);
5981     if (sql_in != NULL)
5982 	sqlite3_free (sql_in);
5983     if (sql_out != NULL)
5984 	sqlite3_free (sql_out);
5985     msg = "SQL/MM Spatial exception - unable to create the dustbin table.";
5986     gaiatopo_set_last_error_msg (accessor, msg);
5987     sqlite3_result_error (context, msg, -1);
5988     return;
5989 
5990   no_dustbin_view:
5991     if (xtable != NULL)
5992 	free (xtable);
5993     if (xcolumn != NULL)
5994 	free (xcolumn);
5995     if (sql_in != NULL)
5996 	sqlite3_free (sql_in);
5997     if (sql_out != NULL)
5998 	sqlite3_free (sql_out);
5999     if (sql_in2 != NULL)
6000 	sqlite3_free (sql_in2);
6001     msg = "SQL/MM Spatial exception - unable to create the dustbin view.";
6002     gaiatopo_set_last_error_msg (accessor, msg);
6003     sqlite3_result_error (context, msg, -1);
6004     return;
6005 
6006   negative_tolerance:
6007     if (xtable != NULL)
6008 	free (xtable);
6009     if (xcolumn != NULL)
6010 	free (xcolumn);
6011     if (sql_in != NULL)
6012 	sqlite3_free (sql_in);
6013     if (sql_out != NULL)
6014 	sqlite3_free (sql_out);
6015     if (sql_in2 != NULL)
6016 	sqlite3_free (sql_in2);
6017     msg = "SQL/MM Spatial exception - illegal negative tolerance.";
6018     gaiatopo_set_last_error_msg (accessor, msg);
6019     sqlite3_result_error (context, msg, -1);
6020     return;
6021 
6022   illegal_max_points:
6023     if (xtable != NULL)
6024 	free (xtable);
6025     if (xcolumn != NULL)
6026 	free (xcolumn);
6027     if (sql_in != NULL)
6028 	sqlite3_free (sql_in);
6029     if (sql_out != NULL)
6030 	sqlite3_free (sql_out);
6031     if (sql_in2 != NULL)
6032 	sqlite3_free (sql_in2);
6033     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
6034     gaiatopo_set_last_error_msg (accessor, msg);
6035     sqlite3_result_error (context, msg, -1);
6036     return;
6037 
6038   nonpositive_max_length:
6039     if (xtable != NULL)
6040 	free (xtable);
6041     if (xcolumn != NULL)
6042 	free (xcolumn);
6043     if (sql_in != NULL)
6044 	sqlite3_free (sql_in);
6045     if (sql_out != NULL)
6046 	sqlite3_free (sql_out);
6047     if (sql_in2 != NULL)
6048 	sqlite3_free (sql_in2);
6049     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
6050     gaiatopo_set_last_error_msg (accessor, msg);
6051     sqlite3_result_error (context, msg, -1);
6052     return;
6053 }
6054 
6055 static int
check_matching_srid(GaiaTopologyAccessorPtr accessor,int srid)6056 check_matching_srid (GaiaTopologyAccessorPtr accessor, int srid)
6057 {
6058 /* checking for matching SRID */
6059     struct gaia_topology *topo = (struct gaia_topology *) accessor;
6060     if (topo->srid != srid)
6061 	return 0;
6062     return 1;
6063 }
6064 
6065 SPATIALITE_PRIVATE int
gaia_check_reference_geo_table(const void * handle,const char * db_prefix,const char * table,const char * column,char ** xtable,char ** xcolumn,int * srid,int * family)6066 gaia_check_reference_geo_table (const void *handle, const char *db_prefix,
6067 				const char *table, const char *column,
6068 				char **xtable, char **xcolumn, int *srid,
6069 				int *family)
6070 {
6071     sqlite3 *sqlite = (sqlite3 *) handle;
6072     int dims;
6073     return check_input_geo_table (sqlite, db_prefix, table, column, xtable,
6074 				  xcolumn, srid, family, &dims);
6075 }
6076 
6077 static int
check_reference_table(sqlite3 * sqlite,const char * db_prefix,const char * table)6078 check_reference_table (sqlite3 * sqlite, const char *db_prefix,
6079 		       const char *table)
6080 {
6081 /* checking if an input GeoTable do really exist */
6082 
6083     int ret;
6084     int i;
6085     char **results;
6086     int rows;
6087     int columns;
6088     char *errMsg = NULL;
6089     char *sql;
6090     char *xprefix;
6091     int count = 0;
6092     char *xtable;
6093 
6094 /* testing if the Table do really exist */
6095     xprefix = gaiaDoubleQuotedSql (db_prefix);
6096     xtable = gaiaDoubleQuotedSql (table);
6097     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, xtable);
6098     free (xprefix);
6099     free (xtable);
6100     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
6101     sqlite3_free (sql);
6102     if (ret != SQLITE_OK)
6103       {
6104 	  sqlite3_free (errMsg);
6105 	  return 0;
6106       }
6107     for (i = 1; i <= rows; i++)
6108 	count++;
6109     sqlite3_free_table (results);
6110 
6111     if (count < 1)
6112 	return 0;
6113     return 1;
6114 }
6115 
6116 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_ToGeoTable(const void * xcontext,int argc,const void * xargv)6117 fnctaux_TopoGeo_ToGeoTable (const void *xcontext, int argc, const void *xargv)
6118 {
6119 /* SQL function:
6120 / TopoGeo_ToGeoTable ( text topology-name, text db-prefix, text ref_table,
6121 /                      text ref_column, text out_table )
6122 / TopoGeo_ToGeoTable ( text topology-name, text db-prefix, text ref_table,
6123 /                      text ref_column, text out_table, int with-spatial-index )
6124 /
6125 / returns: 1 on success
6126 / raises an exception on failure
6127 */
6128     const char *msg;
6129     int ret;
6130     const char *topo_name;
6131     const char *db_prefix;
6132     const char *ref_table;
6133     const char *ref_column;
6134     const char *out_table;
6135     int with_spatial_index = 0;
6136     char *xreftable = NULL;
6137     char *xrefcolumn = NULL;
6138     int srid;
6139     int family;
6140     GaiaTopologyAccessorPtr accessor = NULL;
6141     sqlite3_context *context = (sqlite3_context *) xcontext;
6142     sqlite3_value **argv = (sqlite3_value **) xargv;
6143     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6144     struct splite_internal_cache *cache = sqlite3_user_data (context);
6145     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6146     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6147 	goto null_arg;
6148     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6149 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6150     else
6151 	goto invalid_arg;
6152     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
6153 	db_prefix = "main";
6154     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
6155 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
6156     else
6157 	goto invalid_arg;
6158     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
6159 	ref_table = (const char *) sqlite3_value_text (argv[2]);
6160     else
6161 	goto invalid_arg;
6162     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
6163 	ref_column = NULL;
6164     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
6165 	ref_column = (const char *) sqlite3_value_text (argv[3]);
6166     else
6167 	goto invalid_arg;
6168     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
6169 	goto null_arg;
6170     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
6171 	out_table = (const char *) sqlite3_value_text (argv[4]);
6172     else
6173 	goto invalid_arg;
6174     if (argc >= 6)
6175       {
6176 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
6177 	      goto null_arg;
6178 	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
6179 	      with_spatial_index = sqlite3_value_int (argv[5]);
6180 	  else
6181 	      goto invalid_arg;
6182       }
6183 
6184 /* attempting to get a Topology Accessor */
6185     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6186     if (accessor == NULL)
6187 	goto no_topo;
6188     gaiatopo_reset_last_error_msg (accessor);
6189 
6190 /* checking the reference GeoTable */
6191     if (!gaia_check_reference_geo_table
6192 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
6193 	 &srid, &family))
6194 	goto no_reference;
6195     if (!check_matching_srid (accessor, srid))
6196 	goto invalid_geom;
6197 
6198 /* checking the output GeoTable */
6199     if (!check_output_geo_table (sqlite, out_table))
6200 	goto err_output;
6201 
6202     start_topo_savepoint (sqlite, cache);
6203     ret =
6204 	gaiaTopoGeo_ToGeoTable (accessor, db_prefix, xreftable, xrefcolumn,
6205 				out_table, with_spatial_index);
6206     if (!ret)
6207 	rollback_topo_savepoint (sqlite, cache);
6208     else
6209 	release_topo_savepoint (sqlite, cache);
6210     free (xreftable);
6211     free (xrefcolumn);
6212     if (!ret)
6213       {
6214 	  msg = gaiaGetRtTopoErrorMsg (cache);
6215 	  gaiatopo_set_last_error_msg (accessor, msg);
6216 	  sqlite3_result_error (context, msg, -1);
6217 	  return;
6218       }
6219     sqlite3_result_int (context, 1);
6220     return;
6221 
6222   no_topo:
6223     if (xreftable != NULL)
6224 	free (xreftable);
6225     if (xrefcolumn != NULL)
6226 	free (xrefcolumn);
6227     msg = "SQL/MM Spatial exception - invalid topology name.";
6228     gaiatopo_set_last_error_msg (accessor, msg);
6229     sqlite3_result_error (context, msg, -1);
6230     return;
6231 
6232   no_reference:
6233     if (xreftable != NULL)
6234 	free (xreftable);
6235     if (xrefcolumn != NULL)
6236 	free (xrefcolumn);
6237     msg = "TopoGeo_ToGeoTable: invalid reference GeoTable.";
6238     gaiatopo_set_last_error_msg (accessor, msg);
6239     sqlite3_result_error (context, msg, -1);
6240     return;
6241 
6242   err_output:
6243     if (xreftable != NULL)
6244 	free (xreftable);
6245     if (xrefcolumn != NULL)
6246 	free (xrefcolumn);
6247     msg = "TopoGeo_ToGeoTable: output GeoTable already exists.";
6248     gaiatopo_set_last_error_msg (accessor, msg);
6249     sqlite3_result_error (context, msg, -1);
6250     return;
6251 
6252   null_arg:
6253     if (xreftable != NULL)
6254 	free (xreftable);
6255     if (xrefcolumn != NULL)
6256 	free (xrefcolumn);
6257     msg = "SQL/MM Spatial exception - null argument.";
6258     gaiatopo_set_last_error_msg (accessor, msg);
6259     sqlite3_result_error (context, msg, -1);
6260     return;
6261 
6262   invalid_arg:
6263     if (xreftable != NULL)
6264 	free (xreftable);
6265     if (xrefcolumn != NULL)
6266 	free (xrefcolumn);
6267     msg = "SQL/MM Spatial exception - invalid argument.";
6268     gaiatopo_set_last_error_msg (accessor, msg);
6269     sqlite3_result_error (context, msg, -1);
6270     return;
6271 
6272   invalid_geom:
6273     if (xreftable != NULL)
6274 	free (xreftable);
6275     if (xrefcolumn != NULL)
6276 	free (xrefcolumn);
6277     msg =
6278 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
6279     gaiatopo_set_last_error_msg (accessor, msg);
6280     sqlite3_result_error (context, msg, -1);
6281     return;
6282 }
6283 
6284 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_PolyFacesList(const void * xcontext,int argc,const void * xargv)6285 fnctaux_TopoGeo_PolyFacesList (const void *xcontext, int argc,
6286 			       const void *xargv)
6287 {
6288 /* SQL function:
6289 / TopoGeo_PolyFacesList ( text topology-name, text db-prefix, text ref_table,
6290 /                         text ref_column, text out_table )
6291 /
6292 / returns: 1 on success
6293 / raises an exception on failure
6294 */
6295     const char *msg;
6296     int ret;
6297     const char *topo_name;
6298     const char *db_prefix;
6299     const char *ref_table;
6300     const char *ref_column;
6301     const char *out_table;
6302     char *xreftable = NULL;
6303     char *xrefcolumn = NULL;
6304     int srid;
6305     int family;
6306     GaiaTopologyAccessorPtr accessor = NULL;
6307     sqlite3_context *context = (sqlite3_context *) xcontext;
6308     sqlite3_value **argv = (sqlite3_value **) xargv;
6309     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6310     struct splite_internal_cache *cache = sqlite3_user_data (context);
6311     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6312     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6313 	goto null_arg;
6314     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6315 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6316     else
6317 	goto invalid_arg;
6318     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
6319 	db_prefix = "main";
6320     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
6321 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
6322     else
6323 	goto invalid_arg;
6324     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
6325 	ref_table = (const char *) sqlite3_value_text (argv[2]);
6326     else
6327 	goto invalid_arg;
6328     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
6329 	ref_column = NULL;
6330     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
6331 	ref_column = (const char *) sqlite3_value_text (argv[3]);
6332     else
6333 	goto invalid_arg;
6334     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
6335 	goto null_arg;
6336     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
6337 	out_table = (const char *) sqlite3_value_text (argv[4]);
6338     else
6339 	goto invalid_arg;
6340 
6341 /* attempting to get a Topology Accessor */
6342     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6343     if (accessor == NULL)
6344 	goto no_topo;
6345     gaiatopo_reset_last_error_msg (accessor);
6346 
6347 /* checking the reference GeoTable */
6348     if (!gaia_check_reference_geo_table
6349 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
6350 	 &srid, &family))
6351 	goto no_reference;
6352     if (!check_matching_srid (accessor, srid))
6353 	goto invalid_geom;
6354     if (family != GAIA_TYPE_POLYGON)
6355 	goto not_polygon;
6356 
6357 /* checking the output Table */
6358     if (!gaia_check_output_table (sqlite, out_table))
6359 	goto err_output;
6360 
6361     start_topo_savepoint (sqlite, cache);
6362     ret =
6363 	gaiaTopoGeo_PolyFacesList (accessor, db_prefix, xreftable, xrefcolumn,
6364 				   out_table);
6365     if (!ret)
6366 	rollback_topo_savepoint (sqlite, cache);
6367     else
6368 	release_topo_savepoint (sqlite, cache);
6369     free (xreftable);
6370     free (xrefcolumn);
6371     if (!ret)
6372       {
6373 	  msg = gaiaGetRtTopoErrorMsg (cache);
6374 	  gaiatopo_set_last_error_msg (accessor, msg);
6375 	  sqlite3_result_error (context, msg, -1);
6376 	  return;
6377       }
6378     sqlite3_result_int (context, 1);
6379     return;
6380 
6381   no_topo:
6382     if (xreftable != NULL)
6383 	free (xreftable);
6384     if (xrefcolumn != NULL)
6385 	free (xrefcolumn);
6386     msg = "SQL/MM Spatial exception - invalid topology name.";
6387     gaiatopo_set_last_error_msg (accessor, msg);
6388     sqlite3_result_error (context, msg, -1);
6389     return;
6390 
6391   no_reference:
6392     if (xreftable != NULL)
6393 	free (xreftable);
6394     if (xrefcolumn != NULL)
6395 	free (xrefcolumn);
6396     msg = "TopoGeo_PolyFacesList: invalid reference GeoTable.";
6397     gaiatopo_set_last_error_msg (accessor, msg);
6398     sqlite3_result_error (context, msg, -1);
6399     return;
6400 
6401   err_output:
6402     if (xreftable != NULL)
6403 	free (xreftable);
6404     if (xrefcolumn != NULL)
6405 	free (xrefcolumn);
6406     msg = "TopoGeo_PolyFacesList: output GeoTable already exists.";
6407     gaiatopo_set_last_error_msg (accessor, msg);
6408     sqlite3_result_error (context, msg, -1);
6409     return;
6410 
6411   null_arg:
6412     if (xreftable != NULL)
6413 	free (xreftable);
6414     if (xrefcolumn != NULL)
6415 	free (xrefcolumn);
6416     msg = "SQL/MM Spatial exception - null argument.";
6417     gaiatopo_set_last_error_msg (accessor, msg);
6418     sqlite3_result_error (context, msg, -1);
6419     return;
6420 
6421   invalid_arg:
6422     if (xreftable != NULL)
6423 	free (xreftable);
6424     if (xrefcolumn != NULL)
6425 	free (xrefcolumn);
6426     msg = "SQL/MM Spatial exception - invalid argument.";
6427     gaiatopo_set_last_error_msg (accessor, msg);
6428     sqlite3_result_error (context, msg, -1);
6429     return;
6430 
6431   invalid_geom:
6432     if (xreftable != NULL)
6433 	free (xreftable);
6434     if (xrefcolumn != NULL)
6435 	free (xrefcolumn);
6436     msg =
6437 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
6438     gaiatopo_set_last_error_msg (accessor, msg);
6439     sqlite3_result_error (context, msg, -1);
6440     return;
6441 
6442   not_polygon:
6443     if (xreftable != NULL)
6444 	free (xreftable);
6445     if (xrefcolumn != NULL)
6446 	free (xrefcolumn);
6447     msg =
6448 	"SQL/MM Spatial exception - invalid reference GeoTable (not of the [MULTI]POLYGON type).";
6449     gaiatopo_set_last_error_msg (accessor, msg);
6450     sqlite3_result_error (context, msg, -1);
6451     return;
6452 }
6453 
6454 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_LineEdgesList(const void * xcontext,int argc,const void * xargv)6455 fnctaux_TopoGeo_LineEdgesList (const void *xcontext, int argc,
6456 			       const void *xargv)
6457 {
6458 /* SQL function:
6459 / TopoGeo_LineEdgesList ( text topology-name, text db-prefix, text ref_table,
6460 /                         text ref_column, text out_table )
6461 /
6462 / returns: 1 on success
6463 / raises an exception on failure
6464 */
6465     const char *msg;
6466     int ret;
6467     const char *topo_name;
6468     const char *db_prefix;
6469     const char *ref_table;
6470     const char *ref_column;
6471     const char *out_table;
6472     char *xreftable = NULL;
6473     char *xrefcolumn = NULL;
6474     int srid;
6475     int family;
6476     GaiaTopologyAccessorPtr accessor = NULL;
6477     sqlite3_context *context = (sqlite3_context *) xcontext;
6478     sqlite3_value **argv = (sqlite3_value **) xargv;
6479     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6480     struct splite_internal_cache *cache = sqlite3_user_data (context);
6481     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6482     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6483 	goto null_arg;
6484     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6485 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6486     else
6487 	goto invalid_arg;
6488     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
6489 	db_prefix = "main";
6490     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
6491 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
6492     else
6493 	goto invalid_arg;
6494     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
6495 	ref_table = (const char *) sqlite3_value_text (argv[2]);
6496     else
6497 	goto invalid_arg;
6498     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
6499 	ref_column = NULL;
6500     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
6501 	ref_column = (const char *) sqlite3_value_text (argv[3]);
6502     else
6503 	goto invalid_arg;
6504     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
6505 	goto null_arg;
6506     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
6507 	out_table = (const char *) sqlite3_value_text (argv[4]);
6508     else
6509 	goto invalid_arg;
6510 
6511 /* attempting to get a Topology Accessor */
6512     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6513     if (accessor == NULL)
6514 	goto no_topo;
6515     gaiatopo_reset_last_error_msg (accessor);
6516 
6517 /* checking the reference GeoTable */
6518     if (!gaia_check_reference_geo_table
6519 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
6520 	 &srid, &family))
6521 	goto no_reference;
6522     if (!check_matching_srid (accessor, srid))
6523 	goto invalid_geom;
6524     if (family != GAIA_TYPE_LINESTRING)
6525 	goto not_linestring;
6526 
6527 /* checking the output Table */
6528     if (!gaia_check_output_table (sqlite, out_table))
6529 	goto err_output;
6530 
6531     start_topo_savepoint (sqlite, cache);
6532     ret =
6533 	gaiaTopoGeo_LineEdgesList (accessor, db_prefix, xreftable, xrefcolumn,
6534 				   out_table);
6535     if (!ret)
6536 	rollback_topo_savepoint (sqlite, cache);
6537     else
6538 	release_topo_savepoint (sqlite, cache);
6539     free (xreftable);
6540     free (xrefcolumn);
6541     if (!ret)
6542       {
6543 	  msg = gaiaGetRtTopoErrorMsg (cache);
6544 	  gaiatopo_set_last_error_msg (accessor, msg);
6545 	  sqlite3_result_error (context, msg, -1);
6546 	  return;
6547       }
6548     sqlite3_result_int (context, 1);
6549     return;
6550 
6551   no_topo:
6552     if (xreftable != NULL)
6553 	free (xreftable);
6554     if (xrefcolumn != NULL)
6555 	free (xrefcolumn);
6556     msg = "SQL/MM Spatial exception - invalid topology name.";
6557     gaiatopo_set_last_error_msg (accessor, msg);
6558     sqlite3_result_error (context, msg, -1);
6559     return;
6560 
6561   no_reference:
6562     if (xreftable != NULL)
6563 	free (xreftable);
6564     if (xrefcolumn != NULL)
6565 	free (xrefcolumn);
6566     msg = "TopoGeo_LineEdgesList: invalid reference GeoTable.";
6567     gaiatopo_set_last_error_msg (accessor, msg);
6568     sqlite3_result_error (context, msg, -1);
6569     return;
6570 
6571   err_output:
6572     if (xreftable != NULL)
6573 	free (xreftable);
6574     if (xrefcolumn != NULL)
6575 	free (xrefcolumn);
6576     msg = "TopoGeo_LineEdgesList: output GeoTable already exists.";
6577     gaiatopo_set_last_error_msg (accessor, msg);
6578     sqlite3_result_error (context, msg, -1);
6579     return;
6580 
6581   null_arg:
6582     if (xreftable != NULL)
6583 	free (xreftable);
6584     if (xrefcolumn != NULL)
6585 	free (xrefcolumn);
6586     msg = "SQL/MM Spatial exception - null argument.";
6587     gaiatopo_set_last_error_msg (accessor, msg);
6588     sqlite3_result_error (context, msg, -1);
6589     return;
6590 
6591   invalid_arg:
6592     if (xreftable != NULL)
6593 	free (xreftable);
6594     if (xrefcolumn != NULL)
6595 	free (xrefcolumn);
6596     msg = "SQL/MM Spatial exception - invalid argument.";
6597     gaiatopo_set_last_error_msg (accessor, msg);
6598     sqlite3_result_error (context, msg, -1);
6599     return;
6600 
6601   invalid_geom:
6602     if (xreftable != NULL)
6603 	free (xreftable);
6604     if (xrefcolumn != NULL)
6605 	free (xrefcolumn);
6606     msg =
6607 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
6608     gaiatopo_set_last_error_msg (accessor, msg);
6609     sqlite3_result_error (context, msg, -1);
6610     return;
6611 
6612   not_linestring:
6613     if (xreftable != NULL)
6614 	free (xreftable);
6615     if (xrefcolumn != NULL)
6616 	free (xrefcolumn);
6617     msg =
6618 	"SQL/MM Spatial exception - invalid reference GeoTable (not of the [MULTI]LINESTRING type).";
6619     gaiatopo_set_last_error_msg (accessor, msg);
6620     sqlite3_result_error (context, msg, -1);
6621     return;
6622 }
6623 
6624 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_ToGeoTableGeneralize(const void * xcontext,int argc,const void * xargv)6625 fnctaux_TopoGeo_ToGeoTableGeneralize (const void *xcontext, int argc,
6626 				      const void *xargv)
6627 {
6628 /* SQL function:
6629 / TopoGeo_ToGeoTableGeneralize ( text topology-name, text db-prefix,
6630 /                                text ref_table, text ref_column,
6631 /                                text out_table, double tolerance )
6632 / TopoGeo_ToGeoTableGeneralize ( text topology-name, text db-prefix,
6633 /                                text ref_table, text ref_column,
6634 /                                text out_table, double tolerance,
6635 /                                int with-spatial-index )
6636 /
6637 / returns: 1 on success
6638 / raises an exception on failure
6639 */
6640     const char *msg;
6641     int ret;
6642     const char *topo_name;
6643     const char *db_prefix;
6644     const char *ref_table;
6645     const char *ref_column;
6646     const char *out_table;
6647     double tolerance = 0.0;
6648     int with_spatial_index = 0;
6649     char *xreftable = NULL;
6650     char *xrefcolumn = NULL;
6651     int srid;
6652     int family;
6653     GaiaTopologyAccessorPtr accessor = NULL;
6654     sqlite3_context *context = (sqlite3_context *) xcontext;
6655     sqlite3_value **argv = (sqlite3_value **) xargv;
6656     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6657     struct splite_internal_cache *cache = sqlite3_user_data (context);
6658     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6659     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6660 	goto null_arg;
6661     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6662 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6663     else
6664 	goto invalid_arg;
6665     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
6666 	db_prefix = "main";
6667     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
6668 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
6669     else
6670 	goto invalid_arg;
6671     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
6672 	ref_table = (const char *) sqlite3_value_text (argv[2]);
6673     else
6674 	goto invalid_arg;
6675     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
6676 	ref_column = NULL;
6677     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
6678 	ref_column = (const char *) sqlite3_value_text (argv[3]);
6679     else
6680 	goto invalid_arg;
6681     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
6682 	goto null_arg;
6683     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
6684 	out_table = (const char *) sqlite3_value_text (argv[4]);
6685     else
6686 	goto invalid_arg;
6687     if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
6688 	goto null_arg;
6689     else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
6690       {
6691 	  int val = sqlite3_value_int (argv[5]);
6692 	  tolerance = val;
6693       }
6694     else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
6695 	tolerance = sqlite3_value_double (argv[5]);
6696     else
6697 	goto invalid_arg;
6698     if (argc >= 7)
6699       {
6700 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
6701 	      goto null_arg;
6702 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
6703 	      with_spatial_index = sqlite3_value_int (argv[6]);
6704 	  else
6705 	      goto invalid_arg;
6706       }
6707 
6708 /* attempting to get a Topology Accessor */
6709     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6710     if (accessor == NULL)
6711 	goto no_topo;
6712     gaiatopo_reset_last_error_msg (accessor);
6713 
6714 /* checking the reference GeoTable */
6715     if (!gaia_check_reference_geo_table
6716 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
6717 	 &srid, &family))
6718 	goto no_reference;
6719     if (!check_matching_srid (accessor, srid))
6720 	goto invalid_geom;
6721 
6722 /* checking the output GeoTable */
6723     if (!check_output_geo_table (sqlite, out_table))
6724 	goto err_output;
6725 
6726     start_topo_savepoint (sqlite, cache);
6727     ret =
6728 	gaiaTopoGeo_ToGeoTableGeneralize (accessor, db_prefix, xreftable,
6729 					  xrefcolumn, out_table, tolerance,
6730 					  with_spatial_index);
6731     if (!ret)
6732 	rollback_topo_savepoint (sqlite, cache);
6733     else
6734 	release_topo_savepoint (sqlite, cache);
6735     free (xreftable);
6736     free (xrefcolumn);
6737     if (!ret)
6738       {
6739 	  msg = gaiaGetRtTopoErrorMsg (cache);
6740 	  gaiatopo_set_last_error_msg (accessor, msg);
6741 	  sqlite3_result_error (context, msg, -1);
6742 	  return;
6743       }
6744     sqlite3_result_int (context, 1);
6745     return;
6746 
6747   no_topo:
6748     if (xreftable != NULL)
6749 	free (xreftable);
6750     if (xrefcolumn != NULL)
6751 	free (xrefcolumn);
6752     msg = "SQL/MM Spatial exception - invalid topology name.";
6753     gaiatopo_set_last_error_msg (accessor, msg);
6754     sqlite3_result_error (context, msg, -1);
6755     return;
6756 
6757   no_reference:
6758     if (xreftable != NULL)
6759 	free (xreftable);
6760     if (xrefcolumn != NULL)
6761 	free (xrefcolumn);
6762     msg = "TopoGeo_ToGeoTableGeneralize: invalid reference GeoTable.";
6763     gaiatopo_set_last_error_msg (accessor, msg);
6764     sqlite3_result_error (context, msg, -1);
6765     return;
6766 
6767   err_output:
6768     if (xreftable != NULL)
6769 	free (xreftable);
6770     if (xrefcolumn != NULL)
6771 	free (xrefcolumn);
6772     msg = "TopoGeo_ToGeoTableGeneralize: output GeoTable already exists.";
6773     gaiatopo_set_last_error_msg (accessor, msg);
6774     sqlite3_result_error (context, msg, -1);
6775     return;
6776 
6777   null_arg:
6778     if (xreftable != NULL)
6779 	free (xreftable);
6780     if (xrefcolumn != NULL)
6781 	free (xrefcolumn);
6782     msg = "SQL/MM Spatial exception - null argument.";
6783     gaiatopo_set_last_error_msg (accessor, msg);
6784     sqlite3_result_error (context, msg, -1);
6785     return;
6786 
6787   invalid_arg:
6788     if (xreftable != NULL)
6789 	free (xreftable);
6790     if (xrefcolumn != NULL)
6791 	free (xrefcolumn);
6792     msg = "SQL/MM Spatial exception - invalid argument.";
6793     gaiatopo_set_last_error_msg (accessor, msg);
6794     sqlite3_result_error (context, msg, -1);
6795     return;
6796 
6797   invalid_geom:
6798     if (xreftable != NULL)
6799 	free (xreftable);
6800     if (xrefcolumn != NULL)
6801 	free (xrefcolumn);
6802     msg =
6803 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
6804     gaiatopo_set_last_error_msg (accessor, msg);
6805     sqlite3_result_error (context, msg, -1);
6806     return;
6807 }
6808 
6809 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_RemoveSmallFaces(const void * xcontext,int argc,const void * xargv)6810 fnctaux_TopoGeo_RemoveSmallFaces (const void *xcontext, int argc,
6811 				  const void *xargv)
6812 {
6813 /* SQL function:
6814 / TopoGeo_RemoveSmallFaces ( text topology-name, double min-circularity )
6815 / TopoGeo_RemoveSmallFaces ( text topology-name, double min-circularity,
6816 /                            double min-area )
6817 /
6818 / returns: 1 on success
6819 / raises an exception on failure
6820 */
6821     const char *msg;
6822     int ret;
6823     const char *topo_name;
6824     double min_circularity;
6825     double min_area = 0.0;
6826     GaiaTopologyAccessorPtr accessor = NULL;
6827     sqlite3_context *context = (sqlite3_context *) xcontext;
6828     sqlite3_value **argv = (sqlite3_value **) xargv;
6829     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6830     struct splite_internal_cache *cache = sqlite3_user_data (context);
6831     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6832     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6833 	goto null_arg;
6834     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6835 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6836     else
6837 	goto invalid_arg;
6838     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
6839 	goto null_arg;
6840     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
6841       {
6842 	  int val = sqlite3_value_int (argv[1]);
6843 	  min_circularity = val;
6844       }
6845     else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
6846 	min_circularity = sqlite3_value_double (argv[1]);
6847     else
6848 	goto invalid_arg;
6849     if (argc >= 3)
6850       {
6851 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
6852 	      goto null_arg;
6853 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
6854 	    {
6855 		int val = sqlite3_value_int (argv[2]);
6856 		min_area = val;
6857 	    }
6858 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
6859 	      min_area = sqlite3_value_double (argv[2]);
6860 	  else
6861 	      goto invalid_arg;
6862       }
6863 
6864 /* attempting to get a Topology Accessor */
6865     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6866     if (accessor == NULL)
6867 	goto no_topo;
6868 
6869     gaiatopo_reset_last_error_msg (accessor);
6870     start_topo_savepoint (sqlite, cache);
6871     ret = gaiaTopoGeo_RemoveSmallFaces (accessor, min_circularity, min_area);
6872     if (!ret)
6873 	rollback_topo_savepoint (sqlite, cache);
6874     else
6875 	release_topo_savepoint (sqlite, cache);
6876     if (!ret)
6877       {
6878 	  msg = gaiaGetRtTopoErrorMsg (cache);
6879 	  gaiatopo_set_last_error_msg (accessor, msg);
6880 	  sqlite3_result_error (context, msg, -1);
6881 	  return;
6882       }
6883     sqlite3_result_int (context, 1);
6884     return;
6885 
6886   no_topo:
6887     msg = "SQL/MM Spatial exception - invalid topology name.";
6888     gaiatopo_set_last_error_msg (accessor, msg);
6889     sqlite3_result_error (context, msg, -1);
6890     return;
6891 
6892   null_arg:
6893     msg = "SQL/MM Spatial exception - null argument.";
6894     gaiatopo_set_last_error_msg (accessor, msg);
6895     sqlite3_result_error (context, msg, -1);
6896     return;
6897 
6898   invalid_arg:
6899     msg = "SQL/MM Spatial exception - invalid argument.";
6900     gaiatopo_set_last_error_msg (accessor, msg);
6901     sqlite3_result_error (context, msg, -1);
6902     return;
6903 }
6904 
6905 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_RemoveDanglingEdges(const void * xcontext,int argc,const void * xargv)6906 fnctaux_TopoGeo_RemoveDanglingEdges (const void *xcontext, int argc,
6907 				     const void *xargv)
6908 {
6909 /* SQL function:
6910 / TopoGeo_RemoveDanglingEdges ( text topology-name )
6911 /
6912 / returns: 1 on success
6913 / raises an exception on failure
6914 */
6915     const char *msg;
6916     int ret;
6917     const char *topo_name;
6918     GaiaTopologyAccessorPtr accessor = NULL;
6919     sqlite3_context *context = (sqlite3_context *) xcontext;
6920     sqlite3_value **argv = (sqlite3_value **) xargv;
6921     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6922     struct splite_internal_cache *cache = sqlite3_user_data (context);
6923     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6924     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6925 	goto null_arg;
6926     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6927 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6928     else
6929 	goto invalid_arg;
6930 
6931 /* attempting to get a Topology Accessor */
6932     accessor = gaiaGetTopology (sqlite, cache, topo_name);
6933     if (accessor == NULL)
6934 	goto no_topo;
6935 
6936     gaiatopo_reset_last_error_msg (accessor);
6937     start_topo_savepoint (sqlite, cache);
6938     ret = gaiaTopoGeo_RemoveDanglingEdges (accessor);
6939     if (!ret)
6940 	rollback_topo_savepoint (sqlite, cache);
6941     else
6942 	release_topo_savepoint (sqlite, cache);
6943     if (!ret)
6944       {
6945 	  msg = gaiaGetRtTopoErrorMsg (cache);
6946 	  gaiatopo_set_last_error_msg (accessor, msg);
6947 	  sqlite3_result_error (context, msg, -1);
6948 	  return;
6949       }
6950     sqlite3_result_int (context, 1);
6951     return;
6952 
6953   no_topo:
6954     msg = "SQL/MM Spatial exception - invalid topology name.";
6955     gaiatopo_set_last_error_msg (accessor, msg);
6956     sqlite3_result_error (context, msg, -1);
6957     return;
6958 
6959   null_arg:
6960     msg = "SQL/MM Spatial exception - null argument.";
6961     gaiatopo_set_last_error_msg (accessor, msg);
6962     sqlite3_result_error (context, msg, -1);
6963     return;
6964 
6965   invalid_arg:
6966     msg = "SQL/MM Spatial exception - invalid argument.";
6967     gaiatopo_set_last_error_msg (accessor, msg);
6968     sqlite3_result_error (context, msg, -1);
6969     return;
6970 }
6971 
6972 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_RemoveDanglingNodes(const void * xcontext,int argc,const void * xargv)6973 fnctaux_TopoGeo_RemoveDanglingNodes (const void *xcontext, int argc,
6974 				     const void *xargv)
6975 {
6976 /* SQL function:
6977 / TopoGeo_RemoveDanglingNodes ( text topology-name )
6978 /
6979 / returns: 1 on success
6980 / raises an exception on failure
6981 */
6982     const char *msg;
6983     int ret;
6984     const char *topo_name;
6985     GaiaTopologyAccessorPtr accessor = NULL;
6986     sqlite3_context *context = (sqlite3_context *) xcontext;
6987     sqlite3_value **argv = (sqlite3_value **) xargv;
6988     sqlite3 *sqlite = sqlite3_context_db_handle (context);
6989     struct splite_internal_cache *cache = sqlite3_user_data (context);
6990     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
6991     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
6992 	goto null_arg;
6993     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
6994 	topo_name = (const char *) sqlite3_value_text (argv[0]);
6995     else
6996 	goto invalid_arg;
6997 
6998 /* attempting to get a Topology Accessor */
6999     accessor = gaiaGetTopology (sqlite, cache, topo_name);
7000     if (accessor == NULL)
7001 	goto no_topo;
7002 
7003     gaiatopo_reset_last_error_msg (accessor);
7004     start_topo_savepoint (sqlite, cache);
7005     ret = gaiaTopoGeo_RemoveDanglingNodes (accessor);
7006     if (!ret)
7007 	rollback_topo_savepoint (sqlite, cache);
7008     else
7009 	release_topo_savepoint (sqlite, cache);
7010     if (!ret)
7011       {
7012 	  msg = gaiaGetRtTopoErrorMsg (cache);
7013 	  gaiatopo_set_last_error_msg (accessor, msg);
7014 	  sqlite3_result_error (context, msg, -1);
7015 	  return;
7016       }
7017     sqlite3_result_int (context, 1);
7018     return;
7019 
7020   no_topo:
7021     msg = "SQL/MM Spatial exception - invalid topology name.";
7022     gaiatopo_set_last_error_msg (accessor, msg);
7023     sqlite3_result_error (context, msg, -1);
7024     return;
7025 
7026   null_arg:
7027     msg = "SQL/MM Spatial exception - null argument.";
7028     gaiatopo_set_last_error_msg (accessor, msg);
7029     sqlite3_result_error (context, msg, -1);
7030     return;
7031 
7032   invalid_arg:
7033     msg = "SQL/MM Spatial exception - invalid argument.";
7034     gaiatopo_set_last_error_msg (accessor, msg);
7035     sqlite3_result_error (context, msg, -1);
7036     return;
7037 }
7038 
7039 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_NewEdgeHeal(const void * xcontext,int argc,const void * xargv)7040 fnctaux_TopoGeo_NewEdgeHeal (const void *xcontext, int argc, const void *xargv)
7041 {
7042 /* SQL function:
7043 / TopoGeo_NewEdgeHeal ( text topology-name )
7044 /
7045 / returns: 1 on success
7046 / raises an exception on failure
7047 */
7048     const char *msg;
7049     int ret;
7050     const char *topo_name;
7051     GaiaTopologyAccessorPtr accessor = NULL;
7052     sqlite3_context *context = (sqlite3_context *) xcontext;
7053     sqlite3_value **argv = (sqlite3_value **) xargv;
7054     sqlite3 *sqlite = sqlite3_context_db_handle (context);
7055     struct splite_internal_cache *cache = sqlite3_user_data (context);
7056     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
7057     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
7058 	goto null_arg;
7059     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
7060 	topo_name = (const char *) sqlite3_value_text (argv[0]);
7061     else
7062 	goto invalid_arg;
7063 
7064 /* attempting to get a Topology Accessor */
7065     accessor = gaiaGetTopology (sqlite, cache, topo_name);
7066     if (accessor == NULL)
7067 	goto no_topo;
7068 
7069     if (test_inconsistent_topology (accessor) != 0)
7070 	goto inconsistent_topology;
7071 
7072     gaiatopo_reset_last_error_msg (accessor);
7073     start_topo_savepoint (sqlite, cache);
7074     ret = gaiaTopoGeo_NewEdgeHeal (accessor);
7075     if (!ret)
7076 	rollback_topo_savepoint (sqlite, cache);
7077     else
7078 	release_topo_savepoint (sqlite, cache);
7079     if (!ret)
7080       {
7081 	  msg = gaiaGetRtTopoErrorMsg (cache);
7082 	  gaiatopo_set_last_error_msg (accessor, msg);
7083 	  sqlite3_result_error (context, msg, -1);
7084 	  return;
7085       }
7086     sqlite3_result_int (context, 1);
7087     return;
7088 
7089   no_topo:
7090     msg = "SQL/MM Spatial exception - invalid topology name.";
7091     gaiatopo_set_last_error_msg (accessor, msg);
7092     sqlite3_result_error (context, msg, -1);
7093     return;
7094 
7095   null_arg:
7096     msg = "SQL/MM Spatial exception - null argument.";
7097     gaiatopo_set_last_error_msg (accessor, msg);
7098     sqlite3_result_error (context, msg, -1);
7099     return;
7100 
7101   invalid_arg:
7102     msg = "SQL/MM Spatial exception - invalid argument.";
7103     gaiatopo_set_last_error_msg (accessor, msg);
7104     sqlite3_result_error (context, msg, -1);
7105     return;
7106 
7107   inconsistent_topology:
7108     msg =
7109 	"TopoGeo_NewEdgeHeal exception - inconsisten Topology; try executing TopoGeo_Polygonize to recover.";
7110     gaiatopo_set_last_error_msg (accessor, msg);
7111     sqlite3_result_error (context, msg, -1);
7112     return;
7113 }
7114 
7115 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_ModEdgeHeal(const void * xcontext,int argc,const void * xargv)7116 fnctaux_TopoGeo_ModEdgeHeal (const void *xcontext, int argc, const void *xargv)
7117 {
7118 /* SQL function:
7119 / TopoGeo_ModEdgeHeal ( text topology-name )
7120 /
7121 / returns: 1 on success
7122 / raises an exception on failure
7123 */
7124     const char *msg;
7125     int ret;
7126     const char *topo_name;
7127     GaiaTopologyAccessorPtr accessor = NULL;
7128     sqlite3_context *context = (sqlite3_context *) xcontext;
7129     sqlite3_value **argv = (sqlite3_value **) xargv;
7130     sqlite3 *sqlite = sqlite3_context_db_handle (context);
7131     struct splite_internal_cache *cache = sqlite3_user_data (context);
7132     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
7133     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
7134 	goto null_arg;
7135     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
7136 	topo_name = (const char *) sqlite3_value_text (argv[0]);
7137     else
7138 	goto invalid_arg;
7139 
7140 /* attempting to get a Topology Accessor */
7141     accessor = gaiaGetTopology (sqlite, cache, topo_name);
7142     if (accessor == NULL)
7143 	goto no_topo;
7144 
7145     if (test_inconsistent_topology (accessor) != 0)
7146 	goto inconsistent_topology;
7147 
7148     gaiatopo_reset_last_error_msg (accessor);
7149     start_topo_savepoint (sqlite, cache);
7150     ret = gaiaTopoGeo_ModEdgeHeal (accessor);
7151     if (!ret)
7152 	rollback_topo_savepoint (sqlite, cache);
7153     else
7154 	release_topo_savepoint (sqlite, cache);
7155     if (!ret)
7156       {
7157 	  msg = gaiaGetRtTopoErrorMsg (cache);
7158 	  gaiatopo_set_last_error_msg (accessor, msg);
7159 	  sqlite3_result_error (context, msg, -1);
7160 	  return;
7161       }
7162     sqlite3_result_int (context, 1);
7163     return;
7164 
7165   no_topo:
7166     msg = "SQL/MM Spatial exception - invalid topology name.";
7167     gaiatopo_set_last_error_msg (accessor, msg);
7168     sqlite3_result_error (context, msg, -1);
7169     return;
7170 
7171   null_arg:
7172     msg = "SQL/MM Spatial exception - null argument.";
7173     gaiatopo_set_last_error_msg (accessor, msg);
7174     sqlite3_result_error (context, msg, -1);
7175     return;
7176 
7177   invalid_arg:
7178     msg = "SQL/MM Spatial exception - invalid argument.";
7179     gaiatopo_set_last_error_msg (accessor, msg);
7180     sqlite3_result_error (context, msg, -1);
7181     return;
7182 
7183   inconsistent_topology:
7184     msg =
7185 	"TopoGeo_ModEdgeHeal exception - inconsisten Topology; try executing TopoGeo_Polygonize to recover.";
7186     gaiatopo_set_last_error_msg (accessor, msg);
7187     sqlite3_result_error (context, msg, -1);
7188     return;
7189 }
7190 
7191 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_NewEdgesSplit(const void * xcontext,int argc,const void * xargv)7192 fnctaux_TopoGeo_NewEdgesSplit (const void *xcontext, int argc,
7193 			       const void *xargv)
7194 {
7195 /* SQL function:
7196 / TopoGeo_NewEdgesSplit ( text topology-name, int line_max_points )
7197 / TopoGeo_NewEdgesSplit ( text topology-name, int line_max_points,
7198 /                         double max_length )
7199 /
7200 / returns: 1 on success
7201 / raises an exception on failure
7202 */
7203     const char *msg;
7204     int ret;
7205     const char *topo_name;
7206     int line_max_points = -1;
7207     double max_length = -1.0;
7208     GaiaTopologyAccessorPtr accessor = NULL;
7209     sqlite3_context *context = (sqlite3_context *) xcontext;
7210     sqlite3_value **argv = (sqlite3_value **) xargv;
7211     sqlite3 *sqlite = sqlite3_context_db_handle (context);
7212     struct splite_internal_cache *cache = sqlite3_user_data (context);
7213     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
7214     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
7215 	goto null_arg;
7216     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
7217 	topo_name = (const char *) sqlite3_value_text (argv[0]);
7218     else
7219 	goto invalid_arg;
7220     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
7221 	goto null_arg;
7222     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
7223       {
7224 	  line_max_points = sqlite3_value_int (argv[1]);
7225 	  if (line_max_points < 2)
7226 	      goto illegal_max_points;
7227       }
7228     else
7229 	goto invalid_arg;
7230     if (argc >= 3)
7231       {
7232 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
7233 	      goto null_arg;
7234 	  else
7235 	    {
7236 		if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
7237 		  {
7238 		      int max = sqlite3_value_int (argv[2]);
7239 		      max_length = max;
7240 		  }
7241 		else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
7242 		    max_length = sqlite3_value_double (argv[2]);
7243 		else
7244 		    goto invalid_arg;
7245 		if (max_length <= 0.0)
7246 		    goto nonpositive_max_length;
7247 	    }
7248       }
7249 
7250 /* attempting to get a Topology Accessor */
7251     accessor = gaiaGetTopology (sqlite, cache, topo_name);
7252     if (accessor == NULL)
7253 	goto no_topo;
7254 
7255     if (test_inconsistent_topology (accessor) != 0)
7256 	goto inconsistent_topology;
7257 
7258     gaiatopo_reset_last_error_msg (accessor);
7259     start_topo_savepoint (sqlite, cache);
7260     ret = gaiaTopoGeo_NewEdgesSplit (accessor, line_max_points, max_length);
7261     if (!ret)
7262 	rollback_topo_savepoint (sqlite, cache);
7263     else
7264 	release_topo_savepoint (sqlite, cache);
7265     if (!ret)
7266       {
7267 	  msg = gaiaGetRtTopoErrorMsg (cache);
7268 	  gaiatopo_set_last_error_msg (accessor, msg);
7269 	  sqlite3_result_error (context, msg, -1);
7270 	  return;
7271       }
7272     sqlite3_result_int (context, 1);
7273     return;
7274 
7275   no_topo:
7276     msg = "SQL/MM Spatial exception - invalid topology name.";
7277     gaiatopo_set_last_error_msg (accessor, msg);
7278     sqlite3_result_error (context, msg, -1);
7279     return;
7280 
7281   null_arg:
7282     msg = "SQL/MM Spatial exception - null argument.";
7283     gaiatopo_set_last_error_msg (accessor, msg);
7284     sqlite3_result_error (context, msg, -1);
7285     return;
7286 
7287   invalid_arg:
7288     msg = "SQL/MM Spatial exception - invalid argument.";
7289     gaiatopo_set_last_error_msg (accessor, msg);
7290     sqlite3_result_error (context, msg, -1);
7291     return;
7292 
7293   inconsistent_topology:
7294     msg =
7295 	"TopoGeo_NewEdgesSplit exception - inconsisten Topology; try executing TopoGeo_Polygonize to recover.";
7296     gaiatopo_set_last_error_msg (accessor, msg);
7297     sqlite3_result_error (context, msg, -1);
7298     return;
7299 
7300   illegal_max_points:
7301     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
7302     sqlite3_result_error (context, msg, -1);
7303     return;
7304 
7305   nonpositive_max_length:
7306     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
7307     sqlite3_result_error (context, msg, -1);
7308     return;
7309 }
7310 
7311 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_ModEdgeSplit(const void * xcontext,int argc,const void * xargv)7312 fnctaux_TopoGeo_ModEdgeSplit (const void *xcontext, int argc, const void *xargv)
7313 {
7314 /* SQL function:
7315 / TopoGeo_ModEdgeSplit ( text topology-name, int line_max_points )
7316 / TopoGeo_ModEdgeSplit ( text topology-name, int line_max_points,
7317 /                        double max_length )
7318 /
7319 / returns: 1 on success
7320 / raises an exception on failure
7321 */
7322     const char *msg;
7323     int ret;
7324     const char *topo_name;
7325     int line_max_points = -1;
7326     double max_length = -1.0;
7327     GaiaTopologyAccessorPtr accessor = NULL;
7328     sqlite3_context *context = (sqlite3_context *) xcontext;
7329     sqlite3_value **argv = (sqlite3_value **) xargv;
7330     sqlite3 *sqlite = sqlite3_context_db_handle (context);
7331     struct splite_internal_cache *cache = sqlite3_user_data (context);
7332     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
7333     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
7334 	goto null_arg;
7335     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
7336 	topo_name = (const char *) sqlite3_value_text (argv[0]);
7337     else
7338 	goto invalid_arg;
7339     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
7340 	goto null_arg;
7341     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
7342       {
7343 	  line_max_points = sqlite3_value_int (argv[1]);
7344 	  if (line_max_points < 2)
7345 	      goto illegal_max_points;
7346       }
7347     else
7348 	goto invalid_arg;
7349     if (argc >= 3)
7350       {
7351 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
7352 	      goto null_arg;
7353 	  else
7354 	    {
7355 		if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
7356 		  {
7357 		      int max = sqlite3_value_int (argv[2]);
7358 		      max_length = max;
7359 		  }
7360 		else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
7361 		    max_length = sqlite3_value_double (argv[2]);
7362 		else
7363 		    goto invalid_arg;
7364 		if (max_length <= 0.0)
7365 		    goto nonpositive_max_length;
7366 	    }
7367       }
7368 
7369 /* attempting to get a Topology Accessor */
7370     accessor = gaiaGetTopology (sqlite, cache, topo_name);
7371     if (accessor == NULL)
7372 	goto no_topo;
7373 
7374     if (test_inconsistent_topology (accessor) != 0)
7375 	goto inconsistent_topology;
7376 
7377     gaiatopo_reset_last_error_msg (accessor);
7378     start_topo_savepoint (sqlite, cache);
7379     ret = gaiaTopoGeo_ModEdgeSplit (accessor, line_max_points, max_length);
7380     if (!ret)
7381 	rollback_topo_savepoint (sqlite, cache);
7382     else
7383 	release_topo_savepoint (sqlite, cache);
7384     if (!ret)
7385       {
7386 	  msg = gaiaGetRtTopoErrorMsg (cache);
7387 	  gaiatopo_set_last_error_msg (accessor, msg);
7388 	  sqlite3_result_error (context, msg, -1);
7389 	  return;
7390       }
7391     sqlite3_result_int (context, 1);
7392     return;
7393 
7394   no_topo:
7395     msg = "SQL/MM Spatial exception - invalid topology name.";
7396     gaiatopo_set_last_error_msg (accessor, msg);
7397     sqlite3_result_error (context, msg, -1);
7398     return;
7399 
7400   null_arg:
7401     msg = "SQL/MM Spatial exception - null argument.";
7402     gaiatopo_set_last_error_msg (accessor, msg);
7403     sqlite3_result_error (context, msg, -1);
7404     return;
7405 
7406   invalid_arg:
7407     msg = "SQL/MM Spatial exception - invalid argument.";
7408     gaiatopo_set_last_error_msg (accessor, msg);
7409     sqlite3_result_error (context, msg, -1);
7410     return;
7411 
7412   inconsistent_topology:
7413     msg =
7414 	"TopoGeo_ModEdgeSplit exception - inconsisten Topology; try executing TopoGeo_Polygonize to recover.";
7415     gaiatopo_set_last_error_msg (accessor, msg);
7416     sqlite3_result_error (context, msg, -1);
7417     return;
7418 
7419   illegal_max_points:
7420     msg = "SQL/MM Spatial exception - max_points should be >= 2.";
7421     sqlite3_result_error (context, msg, -1);
7422     return;
7423 
7424   nonpositive_max_length:
7425     msg = "SQL/MM Spatial exception - max_length should be > 0.0.";
7426     sqlite3_result_error (context, msg, -1);
7427     return;
7428 }
7429 
7430 static int
do_clone_face(const char * db_prefix,const char * in_topology_name,struct gaia_topology * topo_out)7431 do_clone_face (const char *db_prefix, const char *in_topology_name,
7432 	       struct gaia_topology *topo_out)
7433 {
7434 /* cloning FACE */
7435     char *sql;
7436     char *table;
7437     char *xprefix;
7438     char *xtable;
7439     sqlite3_stmt *stmt_in = NULL;
7440     sqlite3_stmt *stmt_out = NULL;
7441     int ret;
7442 
7443 /* preparing the input SQL statement */
7444     xprefix = gaiaDoubleQuotedSql (db_prefix);
7445     table = sqlite3_mprintf ("%s_face", in_topology_name);
7446     xtable = gaiaDoubleQuotedSql (table);
7447     sqlite3_free (table);
7448     sql =
7449 	sqlite3_mprintf
7450 	("SELECT face_id, mbr FROM \"%s\".\"%s\" WHERE face_id <> 0",
7451 	 xprefix, xtable);
7452     free (xprefix);
7453     free (xtable);
7454     ret =
7455 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_in,
7456 			    NULL);
7457     sqlite3_free (sql);
7458     if (ret != SQLITE_OK)
7459       {
7460 	  spatialite_e ("SELECT FROM \"face\" error: \"%s\"",
7461 			sqlite3_errmsg (topo_out->db_handle));
7462 	  goto error;
7463       }
7464 
7465 /* preparing the output SQL statement */
7466     table = sqlite3_mprintf ("%s_face", topo_out->topology_name);
7467     xtable = gaiaDoubleQuotedSql (table);
7468     sqlite3_free (table);
7469     sql =
7470 	sqlite3_mprintf
7471 	("INSERT INTO MAIN.\"%s\" (face_id, mbr) VALUES (?, ?)", xtable);
7472     free (xtable);
7473     ret =
7474 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_out,
7475 			    NULL);
7476     sqlite3_free (sql);
7477     if (ret != SQLITE_OK)
7478       {
7479 	  spatialite_e ("INSERT INTO \"face\" error: \"%s\"",
7480 			sqlite3_errmsg (topo_out->db_handle));
7481 	  goto error;
7482       }
7483 
7484     sqlite3_reset (stmt_in);
7485     sqlite3_clear_bindings (stmt_in);
7486     while (1)
7487       {
7488 	  /* scrolling the result set rows */
7489 	  ret = sqlite3_step (stmt_in);
7490 	  if (ret == SQLITE_DONE)
7491 	      break;		/* end of result set */
7492 	  if (ret == SQLITE_ROW)
7493 	    {
7494 		sqlite3_reset (stmt_out);
7495 		sqlite3_clear_bindings (stmt_out);
7496 		if (sqlite3_column_type (stmt_in, 0) == SQLITE_INTEGER)
7497 		    sqlite3_bind_int64 (stmt_out, 1,
7498 					sqlite3_column_int64 (stmt_in, 0));
7499 		else
7500 		    goto invalid_value;
7501 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_NULL)
7502 		    sqlite3_bind_null (stmt_out, 2);
7503 		else if (sqlite3_column_type (stmt_in, 1) == SQLITE_BLOB)
7504 		    sqlite3_bind_blob (stmt_out, 2,
7505 				       sqlite3_column_blob (stmt_in, 1),
7506 				       sqlite3_column_bytes (stmt_in, 1),
7507 				       SQLITE_STATIC);
7508 		else
7509 		    goto invalid_value;
7510 		/* inserting into the output table */
7511 		ret = sqlite3_step (stmt_out);
7512 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
7513 		    ;
7514 		else
7515 		  {
7516 		      spatialite_e ("INSERT INTO \"face\" step error: \"%s\"",
7517 				    sqlite3_errmsg (topo_out->db_handle));
7518 		      goto error;
7519 		  }
7520 	    }
7521 	  else
7522 	    {
7523 		spatialite_e ("SELECT FROM \"face\" step error: %s",
7524 			      sqlite3_errmsg (topo_out->db_handle));
7525 		goto error;
7526 	    }
7527       }
7528 
7529     sqlite3_finalize (stmt_in);
7530     sqlite3_finalize (stmt_out);
7531     return 1;
7532 
7533   invalid_value:
7534     spatialite_e ("SELECT FROM \"face\": found an invalid value");
7535 
7536   error:
7537     if (stmt_in != NULL)
7538 	sqlite3_finalize (stmt_in);
7539     if (stmt_out != NULL)
7540 	sqlite3_finalize (stmt_out);
7541     return 0;
7542 }
7543 
7544 static int
do_clone_node(const char * db_prefix,const char * in_topology_name,struct gaia_topology * topo_out)7545 do_clone_node (const char *db_prefix, const char *in_topology_name,
7546 	       struct gaia_topology *topo_out)
7547 {
7548 /* cloning NODE */
7549     char *sql;
7550     char *table;
7551     char *xprefix;
7552     char *xtable;
7553     sqlite3_stmt *stmt_in = NULL;
7554     sqlite3_stmt *stmt_out = NULL;
7555     int ret;
7556 
7557 /* preparing the input SQL statement */
7558     xprefix = gaiaDoubleQuotedSql (db_prefix);
7559     table = sqlite3_mprintf ("%s_node", in_topology_name);
7560     xtable = gaiaDoubleQuotedSql (table);
7561     sqlite3_free (table);
7562     sql =
7563 	sqlite3_mprintf
7564 	("SELECT node_id, containing_face, geom FROM \"%s\".\"%s\"", xprefix,
7565 	 xtable);
7566     free (xprefix);
7567     free (xtable);
7568     ret =
7569 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_in,
7570 			    NULL);
7571     sqlite3_free (sql);
7572     if (ret != SQLITE_OK)
7573       {
7574 	  spatialite_e ("SELECT FROM \"node\" error: \"%s\"",
7575 			sqlite3_errmsg (topo_out->db_handle));
7576 	  goto error;
7577       }
7578 
7579 /* preparing the output SQL statement */
7580     table = sqlite3_mprintf ("%s_node", topo_out->topology_name);
7581     xtable = gaiaDoubleQuotedSql (table);
7582     sqlite3_free (table);
7583     sql =
7584 	sqlite3_mprintf
7585 	("INSERT INTO MAIN.\"%s\" (node_id, containing_face, geom) "
7586 	 "VALUES (?, ?, ?)", xtable);
7587     free (xtable);
7588     ret =
7589 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_out,
7590 			    NULL);
7591     sqlite3_free (sql);
7592     if (ret != SQLITE_OK)
7593       {
7594 	  spatialite_e ("INSERT INTO \"node\" error: \"%s\"",
7595 			sqlite3_errmsg (topo_out->db_handle));
7596 	  goto error;
7597       }
7598 
7599     sqlite3_reset (stmt_in);
7600     sqlite3_clear_bindings (stmt_in);
7601     while (1)
7602       {
7603 	  /* scrolling the result set rows */
7604 	  ret = sqlite3_step (stmt_in);
7605 	  if (ret == SQLITE_DONE)
7606 	      break;		/* end of result set */
7607 	  if (ret == SQLITE_ROW)
7608 	    {
7609 		sqlite3_reset (stmt_out);
7610 		sqlite3_clear_bindings (stmt_out);
7611 		if (sqlite3_column_type (stmt_in, 0) == SQLITE_INTEGER)
7612 		    sqlite3_bind_int64 (stmt_out, 1,
7613 					sqlite3_column_int64 (stmt_in, 0));
7614 		else
7615 		    goto invalid_value;
7616 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_NULL)
7617 		    sqlite3_bind_null (stmt_out, 2);
7618 		else if (sqlite3_column_type (stmt_in, 1) == SQLITE_INTEGER)
7619 		    sqlite3_bind_int64 (stmt_out, 2,
7620 					sqlite3_column_int64 (stmt_in, 1));
7621 		else
7622 		    goto invalid_value;
7623 		if (sqlite3_column_type (stmt_in, 2) == SQLITE_BLOB)
7624 		    sqlite3_bind_blob (stmt_out, 3,
7625 				       sqlite3_column_blob (stmt_in, 2),
7626 				       sqlite3_column_bytes (stmt_in, 2),
7627 				       SQLITE_STATIC);
7628 		else
7629 		    goto invalid_value;
7630 		/* inserting into the output table */
7631 		ret = sqlite3_step (stmt_out);
7632 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
7633 		    ;
7634 		else
7635 		  {
7636 		      spatialite_e ("INSERT INTO \"node\" step error: \"%s\"",
7637 				    sqlite3_errmsg (topo_out->db_handle));
7638 		      goto error;
7639 		  }
7640 	    }
7641 	  else
7642 	    {
7643 		spatialite_e ("SELECT FROM \"node\" step error: %s",
7644 			      sqlite3_errmsg (topo_out->db_handle));
7645 		goto error;
7646 	    }
7647       }
7648 
7649     sqlite3_finalize (stmt_in);
7650     sqlite3_finalize (stmt_out);
7651     return 1;
7652 
7653   invalid_value:
7654     spatialite_e ("SELECT FROM \"node\": found an invalid value");
7655 
7656   error:
7657     if (stmt_in != NULL)
7658 	sqlite3_finalize (stmt_in);
7659     if (stmt_out != NULL)
7660 	sqlite3_finalize (stmt_out);
7661     return 0;
7662 }
7663 
7664 static int
do_clone_edge(const char * db_prefix,const char * in_topology_name,struct gaia_topology * topo_out)7665 do_clone_edge (const char *db_prefix, const char *in_topology_name,
7666 	       struct gaia_topology *topo_out)
7667 {
7668 /* cloning EDGE */
7669     char *sql;
7670     char *table;
7671     char *xprefix;
7672     char *xtable;
7673     sqlite3_stmt *stmt_in = NULL;
7674     sqlite3_stmt *stmt_out = NULL;
7675     int ret;
7676 
7677 /* preparing the input SQL statement */
7678     xprefix = gaiaDoubleQuotedSql (db_prefix);
7679     table = sqlite3_mprintf ("%s_edge", in_topology_name);
7680     xtable = gaiaDoubleQuotedSql (table);
7681     sqlite3_free (table);
7682     sql =
7683 	sqlite3_mprintf
7684 	("SELECT edge_id, start_node, end_node, next_left_edge, "
7685 	 "next_right_edge, left_face, right_face, geom FROM \"%s\".\"%s\"",
7686 	 xprefix, xtable);
7687     free (xprefix);
7688     free (xtable);
7689     ret =
7690 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_in,
7691 			    NULL);
7692     sqlite3_free (sql);
7693     if (ret != SQLITE_OK)
7694       {
7695 	  spatialite_e ("SELECT FROM \"edge\" error: \"%s\"",
7696 			sqlite3_errmsg (topo_out->db_handle));
7697 	  goto error;
7698       }
7699 
7700 /* preparing the output SQL statement */
7701     table = sqlite3_mprintf ("%s_edge", topo_out->topology_name);
7702     xtable = gaiaDoubleQuotedSql (table);
7703     sqlite3_free (table);
7704     sql =
7705 	sqlite3_mprintf
7706 	("INSERT INTO MAIN.\"%s\" (edge_id, start_node, end_node, "
7707 	 "next_left_edge, next_right_edge, left_face, right_face, geom) "
7708 	 "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", xtable);
7709     free (xtable);
7710     ret =
7711 	sqlite3_prepare_v2 (topo_out->db_handle, sql, strlen (sql), &stmt_out,
7712 			    NULL);
7713     sqlite3_free (sql);
7714     if (ret != SQLITE_OK)
7715       {
7716 	  spatialite_e ("INSERT INTO \"edge\" error: \"%s\"",
7717 			sqlite3_errmsg (topo_out->db_handle));
7718 	  goto error;
7719       }
7720 
7721     sqlite3_reset (stmt_in);
7722     sqlite3_clear_bindings (stmt_in);
7723     while (1)
7724       {
7725 	  /* scrolling the result set rows */
7726 	  ret = sqlite3_step (stmt_in);
7727 	  if (ret == SQLITE_DONE)
7728 	      break;		/* end of result set */
7729 	  if (ret == SQLITE_ROW)
7730 	    {
7731 		sqlite3_reset (stmt_out);
7732 		sqlite3_clear_bindings (stmt_out);
7733 		if (sqlite3_column_type (stmt_in, 0) == SQLITE_INTEGER)
7734 		    sqlite3_bind_int64 (stmt_out, 1,
7735 					sqlite3_column_int64 (stmt_in, 0));
7736 		else
7737 		    goto invalid_value;
7738 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_INTEGER)
7739 		    sqlite3_bind_int64 (stmt_out, 2,
7740 					sqlite3_column_int64 (stmt_in, 1));
7741 		else
7742 		    goto invalid_value;
7743 		if (sqlite3_column_type (stmt_in, 2) == SQLITE_INTEGER)
7744 		    sqlite3_bind_int64 (stmt_out, 3,
7745 					sqlite3_column_int64 (stmt_in, 2));
7746 		else
7747 		    goto invalid_value;
7748 		if (sqlite3_column_type (stmt_in, 3) == SQLITE_INTEGER)
7749 		    sqlite3_bind_int64 (stmt_out, 4,
7750 					sqlite3_column_int64 (stmt_in, 3));
7751 		else
7752 		    goto invalid_value;
7753 		if (sqlite3_column_type (stmt_in, 4) == SQLITE_INTEGER)
7754 		    sqlite3_bind_int64 (stmt_out, 5,
7755 					sqlite3_column_int64 (stmt_in, 4));
7756 		else
7757 		    goto invalid_value;
7758 		if (sqlite3_column_type (stmt_in, 5) == SQLITE_INTEGER)
7759 		    sqlite3_bind_int64 (stmt_out, 6,
7760 					sqlite3_column_int64 (stmt_in, 5));
7761 		else
7762 		    goto invalid_value;
7763 		if (sqlite3_column_type (stmt_in, 6) == SQLITE_INTEGER)
7764 		    sqlite3_bind_int64 (stmt_out, 7,
7765 					sqlite3_column_int64 (stmt_in, 6));
7766 		else
7767 		    goto invalid_value;
7768 		if (sqlite3_column_type (stmt_in, 7) == SQLITE_BLOB)
7769 		    sqlite3_bind_blob (stmt_out, 8,
7770 				       sqlite3_column_blob (stmt_in, 7),
7771 				       sqlite3_column_bytes (stmt_in, 7),
7772 				       SQLITE_STATIC);
7773 		else
7774 		    goto invalid_value;
7775 		/* inserting into the output table */
7776 		ret = sqlite3_step (stmt_out);
7777 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
7778 		    ;
7779 		else
7780 		  {
7781 		      spatialite_e ("INSERT INTO \"edge\" step error: \"%s\"",
7782 				    sqlite3_errmsg (topo_out->db_handle));
7783 		      goto error;
7784 		  }
7785 	    }
7786 	  else
7787 	    {
7788 		spatialite_e ("SELECT FROM \"edge\" step error: %s",
7789 			      sqlite3_errmsg (topo_out->db_handle));
7790 		goto error;
7791 	    }
7792       }
7793 
7794     sqlite3_finalize (stmt_in);
7795     sqlite3_finalize (stmt_out);
7796     return 1;
7797 
7798   invalid_value:
7799     spatialite_e ("SELECT FROM \"edge\": found an invalid value");
7800 
7801   error:
7802     if (stmt_in != NULL)
7803 	sqlite3_finalize (stmt_in);
7804     if (stmt_out != NULL)
7805 	sqlite3_finalize (stmt_out);
7806     return 0;
7807 }
7808 
7809 static int
do_clone_topology(const char * db_prefix,const char * in_topology,GaiaTopologyAccessorPtr accessor)7810 do_clone_topology (const char *db_prefix, const char *in_topology,
7811 		   GaiaTopologyAccessorPtr accessor)
7812 {
7813 /* cloning a full Topology */
7814     struct gaia_topology *topo_out = (struct gaia_topology *) accessor;
7815 
7816 /* cloning FACE */
7817     if (!do_clone_face (db_prefix, in_topology, topo_out))
7818 	return 0;
7819 
7820 /* cloning NODE */
7821     if (!do_clone_node (db_prefix, in_topology, topo_out))
7822 	return 0;
7823 
7824 /* cloning EDGE */
7825     if (!do_clone_edge (db_prefix, in_topology, topo_out))
7826 	return 0;
7827 
7828     return 1;
7829 }
7830 
7831 static char *
gaiaGetAttachedTopology(sqlite3 * handle,const char * db_prefix,const char * topo_name,int * srid,double * tolerance,int * has_z)7832 gaiaGetAttachedTopology (sqlite3 * handle, const char *db_prefix,
7833 			 const char *topo_name, int *srid, double *tolerance,
7834 			 int *has_z)
7835 {
7836 /* attempting to retrieve the Input Topology for TopoGeo_Clone */
7837     char *sql;
7838     int ret;
7839     sqlite3_stmt *stmt = NULL;
7840     int ok = 0;
7841     char *xprefix;
7842     char *xtopology_name = NULL;
7843     int xsrid;
7844     double xtolerance;
7845     int xhas_z;
7846 
7847 /* preparing the SQL query */
7848     xprefix = gaiaDoubleQuotedSql (db_prefix);
7849     sql =
7850 	sqlite3_mprintf
7851 	("SELECT topology_name, srid, tolerance, has_z FROM \"%s\".topologies WHERE "
7852 	 "Lower(topology_name) = Lower(%Q)", xprefix, topo_name);
7853     free (xprefix);
7854     ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
7855     sqlite3_free (sql);
7856     if (ret != SQLITE_OK)
7857       {
7858 	  spatialite_e ("SELECT FROM topologys error: \"%s\"\n",
7859 			sqlite3_errmsg (handle));
7860 	  return NULL;
7861       }
7862 
7863     while (1)
7864       {
7865 	  /* scrolling the result set rows */
7866 	  ret = sqlite3_step (stmt);
7867 	  if (ret == SQLITE_DONE)
7868 	      break;		/* end of result set */
7869 	  if (ret == SQLITE_ROW)
7870 	    {
7871 		int ok_name = 0;
7872 		int ok_srid = 0;
7873 		int ok_tolerance = 0;
7874 		int ok_z = 0;
7875 		if (sqlite3_column_type (stmt, 0) == SQLITE_TEXT)
7876 		  {
7877 		      const char *str =
7878 			  (const char *) sqlite3_column_text (stmt, 0);
7879 		      if (xtopology_name != NULL)
7880 			  free (xtopology_name);
7881 		      xtopology_name = malloc (strlen (str) + 1);
7882 		      strcpy (xtopology_name, str);
7883 		      ok_name = 1;
7884 		  }
7885 		if (sqlite3_column_type (stmt, 1) == SQLITE_INTEGER)
7886 		  {
7887 		      xsrid = sqlite3_column_int (stmt, 1);
7888 		      ok_srid = 1;
7889 		  }
7890 		if (sqlite3_column_type (stmt, 2) == SQLITE_FLOAT)
7891 		  {
7892 		      xtolerance = sqlite3_column_double (stmt, 2);
7893 		      ok_tolerance = 1;
7894 		  }
7895 		if (sqlite3_column_type (stmt, 3) == SQLITE_INTEGER)
7896 		  {
7897 		      xhas_z = sqlite3_column_int (stmt, 3);
7898 		      ok_z = 1;
7899 		  }
7900 		if (ok_name && ok_srid && ok_tolerance && ok_z)
7901 		  {
7902 		      ok = 1;
7903 		      break;
7904 		  }
7905 	    }
7906 	  else
7907 	    {
7908 		spatialite_e
7909 		    ("step: SELECT FROM topologies error: \"%s\"\n",
7910 		     sqlite3_errmsg (handle));
7911 		sqlite3_finalize (stmt);
7912 		return NULL;
7913 	    }
7914       }
7915     sqlite3_finalize (stmt);
7916 
7917     if (ok)
7918       {
7919 	  *srid = xsrid;
7920 	  *tolerance = xtolerance;
7921 	  *has_z = xhas_z;
7922 	  return xtopology_name;
7923       }
7924 
7925     if (xtopology_name != NULL)
7926 	free (xtopology_name);
7927     return NULL;
7928 }
7929 
7930 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_Clone(const void * xcontext,int argc,const void * xargv)7931 fnctaux_TopoGeo_Clone (const void *xcontext, int argc, const void *xargv)
7932 {
7933 /* SQL function:
7934 / TopoGeo_Clone ( text db-prefix, text in-topology-name, text out-topology-name )
7935 /
7936 / returns: 1 on success
7937 / raises an exception on failure
7938 */
7939     const char *msg;
7940     int ret;
7941     const char *db_prefix = "MAIN";
7942     const char *in_topo_name;
7943     const char *out_topo_name;
7944     char *input_topo_name = NULL;
7945     int srid;
7946     double tolerance;
7947     int has_z;
7948     GaiaTopologyAccessorPtr accessor = NULL;
7949     sqlite3_context *context = (sqlite3_context *) xcontext;
7950     sqlite3_value **argv = (sqlite3_value **) xargv;
7951     sqlite3 *sqlite = sqlite3_context_db_handle (context);
7952     struct splite_internal_cache *cache = sqlite3_user_data (context);
7953     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
7954     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
7955 	;
7956     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
7957 	db_prefix = (const char *) sqlite3_value_text (argv[0]);
7958     else
7959 	goto invalid_arg;
7960     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
7961 	goto null_arg;
7962     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
7963 	in_topo_name = (const char *) sqlite3_value_text (argv[1]);
7964     else
7965 	goto invalid_arg;
7966     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
7967 	goto null_arg;
7968     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
7969 	out_topo_name = (const char *) sqlite3_value_text (argv[2]);
7970     else
7971 	goto invalid_arg;
7972 
7973 /* checking the origin Topology */
7974     input_topo_name =
7975 	gaiaGetAttachedTopology (sqlite, db_prefix, in_topo_name, &srid,
7976 				 &tolerance, &has_z);
7977     if (input_topo_name == NULL)
7978 	goto no_topo;
7979 
7980 /* attempting to create the destination Topology */
7981     start_topo_savepoint (sqlite, cache);
7982     ret = gaiaTopologyCreate (sqlite, out_topo_name, srid, tolerance, has_z);
7983     if (!ret)
7984       {
7985 	  rollback_topo_savepoint (sqlite, cache);
7986 	  goto no_topo2;
7987       }
7988 
7989 /* attempting to get a Topology Accessor (destination) */
7990     accessor = gaiaGetTopology (sqlite, cache, out_topo_name);
7991     if (accessor == NULL)
7992 	goto no_topo2;
7993     gaiatopo_reset_last_error_msg (accessor);
7994 
7995 /* cloning Topology */
7996     ret = do_clone_topology (db_prefix, input_topo_name, accessor);
7997     if (!ret)
7998 	rollback_topo_savepoint (sqlite, cache);
7999     else
8000 	release_topo_savepoint (sqlite, cache);
8001     if (!ret)
8002       {
8003 	  sqlite3_result_error (context, "Clone Topology failure", -1);
8004 	  return;
8005       }
8006     sqlite3_result_int (context, 1);
8007     if (input_topo_name != NULL)
8008 	free (input_topo_name);
8009     return;
8010 
8011   no_topo:
8012     if (input_topo_name != NULL)
8013 	free (input_topo_name);
8014     msg = "SQL/MM Spatial exception - invalid topology name (origin).";
8015     gaiatopo_set_last_error_msg (accessor, msg);
8016     sqlite3_result_error (context, msg, -1);
8017     return;
8018 
8019   no_topo2:
8020     if (input_topo_name != NULL)
8021 	free (input_topo_name);
8022     msg = "SQL/MM Spatial exception - invalid topology name (destination).";
8023     gaiatopo_set_last_error_msg (accessor, msg);
8024     sqlite3_result_error (context, msg, -1);
8025     return;
8026 
8027   null_arg:
8028     if (input_topo_name != NULL)
8029 	free (input_topo_name);
8030     msg = "SQL/MM Spatial exception - null argument.";
8031     gaiatopo_set_last_error_msg (accessor, msg);
8032     sqlite3_result_error (context, msg, -1);
8033     return;
8034 
8035   invalid_arg:
8036     if (input_topo_name != NULL)
8037 	free (input_topo_name);
8038     msg = "SQL/MM Spatial exception - invalid argument.";
8039     gaiatopo_set_last_error_msg (accessor, msg);
8040     sqlite3_result_error (context, msg, -1);
8041     return;
8042 }
8043 
8044 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_GetEdgeSeed(const void * xcontext,int argc,const void * xargv)8045 fnctaux_TopoGeo_GetEdgeSeed (const void *xcontext, int argc, const void *xargv)
8046 {
8047 /* SQL function:
8048 / TopoGeo_GetEdgeSeed ( text topology-name, int edge_id )
8049 /
8050 / returns: a Point (seed) identifying the Edge
8051 / raises an exception on failure
8052 */
8053     const char *msg;
8054     const char *topo_name;
8055     sqlite3_int64 edge_id;
8056     unsigned char *p_blob;
8057     int n_bytes;
8058     gaiaGeomCollPtr geom;
8059     GaiaTopologyAccessorPtr accessor = NULL;
8060     int gpkg_mode = 0;
8061     int tiny_point = 0;
8062     sqlite3_context *context = (sqlite3_context *) xcontext;
8063     sqlite3_value **argv = (sqlite3_value **) xargv;
8064     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8065     struct splite_internal_cache *cache = sqlite3_user_data (context);
8066     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8067     if (cache != NULL)
8068       {
8069 	  gpkg_mode = cache->gpkg_mode;
8070 	  tiny_point = cache->tinyPointEnabled;
8071       }
8072     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8073 	goto null_arg;
8074     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
8075 	topo_name = (const char *) sqlite3_value_text (argv[0]);
8076     else
8077 	goto invalid_arg;
8078     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8079 	goto null_arg;
8080     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
8081 	edge_id = sqlite3_value_int64 (argv[1]);
8082     else
8083 	goto invalid_arg;
8084 
8085 /* attempting to get a Topology Accessor */
8086     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8087     if (accessor == NULL)
8088 	goto no_topo;
8089 
8090     gaiatopo_reset_last_error_msg (accessor);
8091     geom = gaiaGetEdgeSeed (accessor, edge_id);
8092     if (geom == NULL)
8093       {
8094 	  msg = gaiaGetRtTopoErrorMsg (cache);
8095 	  if (msg != NULL)
8096 	    {
8097 		gaiatopo_set_last_error_msg (accessor, msg);
8098 		sqlite3_result_error (context, msg, -1);
8099 		return;
8100 	    }
8101 	  sqlite3_result_null (context);
8102 	  return;
8103       }
8104     gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode, tiny_point);
8105     gaiaFreeGeomColl (geom);
8106     if (p_blob == NULL)
8107 	sqlite3_result_null (context);
8108     else
8109 	sqlite3_result_blob (context, p_blob, n_bytes, free);
8110     return;
8111 
8112   no_topo:
8113     msg = "SQL/MM Spatial exception - invalid topology name.";
8114     gaiatopo_set_last_error_msg (accessor, msg);
8115     sqlite3_result_error (context, msg, -1);
8116     return;
8117 
8118   null_arg:
8119     msg = "SQL/MM Spatial exception - null argument.";
8120     gaiatopo_set_last_error_msg (accessor, msg);
8121     sqlite3_result_error (context, msg, -1);
8122     return;
8123 
8124   invalid_arg:
8125     msg = "SQL/MM Spatial exception - invalid argument.";
8126     gaiatopo_set_last_error_msg (accessor, msg);
8127     sqlite3_result_error (context, msg, -1);
8128     return;
8129 }
8130 
8131 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_GetFaceSeed(const void * xcontext,int argc,const void * xargv)8132 fnctaux_TopoGeo_GetFaceSeed (const void *xcontext, int argc, const void *xargv)
8133 {
8134 /* SQL function:
8135 / TopoGeo_GetFaceSeed ( text topology-name, int face_id )
8136 /
8137 / returns: a Point (seed) identifying the Edge
8138 / raises an exception on failure
8139 */
8140     const char *msg;
8141     const char *topo_name;
8142     sqlite3_int64 face_id;
8143     unsigned char *p_blob;
8144     int n_bytes;
8145     gaiaGeomCollPtr geom;
8146     GaiaTopologyAccessorPtr accessor = NULL;
8147     int gpkg_mode = 0;
8148     int tiny_point = 0;
8149     sqlite3_context *context = (sqlite3_context *) xcontext;
8150     sqlite3_value **argv = (sqlite3_value **) xargv;
8151     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8152     struct splite_internal_cache *cache = sqlite3_user_data (context);
8153     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8154     if (cache != NULL)
8155       {
8156 	  gpkg_mode = cache->gpkg_mode;
8157 	  tiny_point = cache->tinyPointEnabled;
8158       }
8159     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8160 	goto null_arg;
8161     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
8162 	topo_name = (const char *) sqlite3_value_text (argv[0]);
8163     else
8164 	goto invalid_arg;
8165     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8166 	goto null_arg;
8167     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
8168 	face_id = sqlite3_value_int64 (argv[1]);
8169     else
8170 	goto invalid_arg;
8171 
8172 /* attempting to get a Topology Accessor */
8173     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8174     if (accessor == NULL)
8175 	goto no_topo;
8176 
8177     gaiatopo_reset_last_error_msg (accessor);
8178     geom = gaiaGetFaceSeed (accessor, face_id);
8179     if (geom == NULL)
8180       {
8181 	  msg = gaiaGetRtTopoErrorMsg (cache);
8182 	  if (msg != NULL)
8183 	    {
8184 		gaiatopo_set_last_error_msg (accessor, msg);
8185 		sqlite3_result_error (context, msg, -1);
8186 		return;
8187 	    }
8188 	  sqlite3_result_null (context);
8189 	  return;
8190       }
8191     gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode, tiny_point);
8192     gaiaFreeGeomColl (geom);
8193     if (p_blob == NULL)
8194 	sqlite3_result_null (context);
8195     else
8196 	sqlite3_result_blob (context, p_blob, n_bytes, free);
8197     return;
8198 
8199   no_topo:
8200     msg = "SQL/MM Spatial exception - invalid topology name.";
8201     gaiatopo_set_last_error_msg (accessor, msg);
8202     sqlite3_result_error (context, msg, -1);
8203     return;
8204 
8205   null_arg:
8206     msg = "SQL/MM Spatial exception - null argument.";
8207     gaiatopo_set_last_error_msg (accessor, msg);
8208     sqlite3_result_error (context, msg, -1);
8209     return;
8210 
8211   invalid_arg:
8212     msg = "SQL/MM Spatial exception - invalid argument.";
8213     gaiatopo_set_last_error_msg (accessor, msg);
8214     sqlite3_result_error (context, msg, -1);
8215     return;
8216 }
8217 
8218 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_DisambiguateSegmentEdges(const void * xcontext,int argc,const void * xargv)8219 fnctaux_TopoGeo_DisambiguateSegmentEdges (const void *xcontext, int argc,
8220 					  const void *xargv)
8221 {
8222 /* SQL function:
8223 / TopoGeo_DisambiguateSegmentEdges ( text topology-name )
8224 /
8225 / returns: the total number of changed Edges.
8226 / raises an exception on failure
8227 */
8228     const char *msg;
8229     const char *topo_name;
8230     int changed_edges = 0;
8231     GaiaTopologyAccessorPtr accessor = NULL;
8232     sqlite3_context *context = (sqlite3_context *) xcontext;
8233     sqlite3_value **argv = (sqlite3_value **) xargv;
8234     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8235     struct splite_internal_cache *cache = sqlite3_user_data (context);
8236     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8237     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8238 	goto null_arg;
8239     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
8240 	topo_name = (const char *) sqlite3_value_text (argv[0]);
8241     else
8242 	goto invalid_arg;
8243 
8244 /* attempting to get a Topology Accessor */
8245     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8246     if (accessor == NULL)
8247 	goto no_topo;
8248 
8249     gaiatopo_reset_last_error_msg (accessor);
8250     start_topo_savepoint (sqlite, cache);
8251     changed_edges = gaiaTopoGeo_DisambiguateSegmentEdges (accessor);
8252     if (changed_edges < 0)
8253 	rollback_topo_savepoint (sqlite, cache);
8254     else
8255 	release_topo_savepoint (sqlite, cache);
8256     if (changed_edges < 0)
8257       {
8258 	  msg = gaiaGetRtTopoErrorMsg (cache);
8259 	  if (msg != NULL)
8260 	    {
8261 		gaiatopo_set_last_error_msg (accessor, msg);
8262 		sqlite3_result_error (context, msg, -1);
8263 		return;
8264 	    }
8265 	  sqlite3_result_null (context);
8266 	  return;
8267       }
8268     sqlite3_result_int (context, changed_edges);
8269     return;
8270 
8271   no_topo:
8272     msg = "SQL/MM Spatial exception - invalid topology name.";
8273     gaiatopo_set_last_error_msg (accessor, msg);
8274     sqlite3_result_error (context, msg, -1);
8275     return;
8276 
8277   null_arg:
8278     msg = "SQL/MM Spatial exception - null argument.";
8279     gaiatopo_set_last_error_msg (accessor, msg);
8280     sqlite3_result_error (context, msg, -1);
8281     return;
8282 
8283   invalid_arg:
8284     msg = "SQL/MM Spatial exception - invalid argument.";
8285     gaiatopo_set_last_error_msg (accessor, msg);
8286     sqlite3_result_error (context, msg, -1);
8287     return;
8288 }
8289 
8290 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_UpdateSeeds(const void * xcontext,int argc,const void * xargv)8291 fnctaux_TopoGeo_UpdateSeeds (const void *xcontext, int argc, const void *xargv)
8292 {
8293 /* SQL function:
8294 / TopoGeo_UpdateSeeds ( text topology-name )
8295 / TopoGeo_UpdateSeeds ( text topology-name, int incremental_mode )
8296 /
8297 / returns: 1 on success
8298 / raises an exception on failure
8299 */
8300     const char *msg;
8301     const char *topo_name;
8302     int incremental_mode = 1;
8303     int ret;
8304     GaiaTopologyAccessorPtr accessor = NULL;
8305     sqlite3_context *context = (sqlite3_context *) xcontext;
8306     sqlite3_value **argv = (sqlite3_value **) xargv;
8307     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8308     struct splite_internal_cache *cache = sqlite3_user_data (context);
8309     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8310     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8311 	goto null_arg;
8312     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
8313 	topo_name = (const char *) sqlite3_value_text (argv[0]);
8314     else
8315 	goto invalid_arg;
8316     if (argc >= 2)
8317       {
8318 	  if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8319 	      goto null_arg;
8320 	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
8321 	      incremental_mode = sqlite3_value_int (argv[1]);
8322 	  else
8323 	      goto invalid_arg;
8324       }
8325 
8326 /* attempting to get a Topology Accessor */
8327     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8328     if (accessor == NULL)
8329 	goto no_topo;
8330 
8331     gaiatopo_reset_last_error_msg (accessor);
8332     start_topo_savepoint (sqlite, cache);
8333     ret = gaiaTopoGeoUpdateSeeds (accessor, incremental_mode);
8334     if (!ret)
8335 	rollback_topo_savepoint (sqlite, cache);
8336     else
8337 	release_topo_savepoint (sqlite, cache);
8338     if (!ret)
8339       {
8340 	  msg = gaiaGetRtTopoErrorMsg (cache);
8341 	  if (msg != NULL)
8342 	    {
8343 		gaiatopo_set_last_error_msg (accessor, msg);
8344 		sqlite3_result_error (context, msg, -1);
8345 		return;
8346 	    }
8347 	  sqlite3_result_null (context);
8348 	  return;
8349       }
8350     sqlite3_result_int (context, 1);
8351     return;
8352 
8353   no_topo:
8354     msg = "SQL/MM Spatial exception - invalid topology name.";
8355     gaiatopo_set_last_error_msg (accessor, msg);
8356     sqlite3_result_error (context, msg, -1);
8357     return;
8358 
8359   null_arg:
8360     msg = "SQL/MM Spatial exception - null argument.";
8361     gaiatopo_set_last_error_msg (accessor, msg);
8362     sqlite3_result_error (context, msg, -1);
8363     return;
8364 
8365   invalid_arg:
8366     msg = "SQL/MM Spatial exception - invalid argument.";
8367     gaiatopo_set_last_error_msg (accessor, msg);
8368     sqlite3_result_error (context, msg, -1);
8369     return;
8370 }
8371 
8372 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_SnapPointToSeed(const void * xcontext,int argc,const void * xargv)8373 fnctaux_TopoGeo_SnapPointToSeed (const void *xcontext, int argc,
8374 				 const void *xargv)
8375 {
8376 /* SQL function:
8377 / TopoGeo_SnapPointToSeed ( geometry point, text topology-name, double distance )
8378 /
8379 / returns: a snapped point geometry
8380 / raises an exception on failure
8381 */
8382     const char *msg;
8383     const char *topo_name;
8384     GaiaTopologyAccessorPtr accessor = NULL;
8385     gaiaGeomCollPtr geom = NULL;
8386     gaiaGeomCollPtr result = NULL;
8387     double dist;
8388     unsigned char *p_blob;
8389     int n_bytes;
8390     int invalid = 0;
8391     int gpkg_amphibious = 0;
8392     int gpkg_mode = 0;
8393     int tiny_point = 0;
8394     sqlite3_context *context = (sqlite3_context *) xcontext;
8395     sqlite3_value **argv = (sqlite3_value **) xargv;
8396     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8397     struct splite_internal_cache *cache = sqlite3_user_data (context);
8398     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8399 
8400     if (cache != NULL)
8401       {
8402 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
8403 	  gpkg_mode = cache->gpkg_mode;
8404 	  tiny_point = cache->tinyPointEnabled;
8405       }
8406 
8407     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8408 	goto null_arg;
8409     if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
8410       {
8411 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
8412 	  n_bytes = sqlite3_value_bytes (argv[0]);
8413       }
8414     else
8415 	goto invalid_arg;
8416     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8417 	goto null_arg;
8418     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
8419 	topo_name = (const char *) sqlite3_value_text (argv[1]);
8420     else
8421 	goto invalid_arg;
8422     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
8423 	goto null_arg;
8424     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
8425       {
8426 	  int intval = sqlite3_value_int (argv[2]);
8427 	  dist = intval;
8428       }
8429     else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
8430 	dist = sqlite3_value_double (argv[2]);
8431     else
8432 	goto invalid_arg;
8433 
8434 /* attempting to get a Point Geometry */
8435     geom =
8436 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
8437 				     gpkg_amphibious);
8438     if (!geom)
8439 	goto invalid_arg;
8440     if (geom->FirstLinestring != NULL)
8441 	invalid = 1;
8442     if (geom->FirstPolygon != NULL)
8443 	invalid = 1;
8444     if (geom->FirstPoint != geom->LastPoint || geom->FirstPoint == NULL)
8445 	invalid = 1;
8446     if (invalid)
8447 	goto invalid_arg;
8448 
8449 /* attempting to get a Topology Accessor */
8450     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8451     if (accessor == NULL)
8452 	goto no_topo;
8453     gaiatopo_reset_last_error_msg (accessor);
8454     if (!check_matching_srid_dims (accessor, geom->Srid, geom->DimensionModel))
8455 	goto invalid_geom;
8456 
8457     result = gaiaTopoGeoSnapPointToSeed (accessor, geom, dist);
8458     if (result == NULL)
8459       {
8460 	  gaiaFreeGeomColl (geom);
8461 	  sqlite3_result_null (context);
8462 	  return;
8463       }
8464     gaiaToSpatiaLiteBlobWkbEx2 (result, &p_blob, &n_bytes, gpkg_mode,
8465 				tiny_point);
8466     gaiaFreeGeomColl (geom);
8467     gaiaFreeGeomColl (result);
8468     if (p_blob == NULL)
8469 	sqlite3_result_null (context);
8470     else
8471 	sqlite3_result_blob (context, p_blob, n_bytes, free);
8472     return;
8473 
8474   no_topo:
8475     if (geom != NULL)
8476 	gaiaFreeGeomColl (geom);
8477     if (result != NULL)
8478 	gaiaFreeGeomColl (result);
8479     msg = "SQL/MM Spatial exception - invalid topology name.";
8480     gaiatopo_set_last_error_msg (accessor, msg);
8481     sqlite3_result_error (context, msg, -1);
8482     return;
8483 
8484   null_arg:
8485     if (geom != NULL)
8486 	gaiaFreeGeomColl (geom);
8487     if (result != NULL)
8488 	gaiaFreeGeomColl (result);
8489     msg = "SQL/MM Spatial exception - null argument.";
8490     gaiatopo_set_last_error_msg (accessor, msg);
8491     sqlite3_result_error (context, msg, -1);
8492     return;
8493 
8494   invalid_arg:
8495     if (geom != NULL)
8496 	gaiaFreeGeomColl (geom);
8497     if (result != NULL)
8498 	gaiaFreeGeomColl (result);
8499     msg = "SQL/MM Spatial exception - invalid argument.";
8500     gaiatopo_set_last_error_msg (accessor, msg);
8501     sqlite3_result_error (context, msg, -1);
8502     return;
8503 
8504   invalid_geom:
8505     if (geom != NULL)
8506 	gaiaFreeGeomColl (geom);
8507     if (result != NULL)
8508 	gaiaFreeGeomColl (result);
8509     msg =
8510 	"SQL/MM Spatial exception - invalid Point (mismatching SRID or dimensions).";
8511     gaiatopo_set_last_error_msg (accessor, msg);
8512     sqlite3_result_error (context, msg, -1);
8513     return;
8514 }
8515 
8516 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_SnapLineToSeed(const void * xcontext,int argc,const void * xargv)8517 fnctaux_TopoGeo_SnapLineToSeed (const void *xcontext, int argc,
8518 				const void *xargv)
8519 {
8520 /* SQL function:
8521 / TopoGeo_SnapLineToSeed ( geometry line, text topology-name, double distance )
8522 /
8523 / returns: a snapped linestring geometry
8524 / raises an exception on failure
8525 */
8526     const char *msg;
8527     const char *topo_name;
8528     GaiaTopologyAccessorPtr accessor = NULL;
8529     gaiaGeomCollPtr geom = NULL;
8530     gaiaGeomCollPtr result = NULL;
8531     double dist;
8532     unsigned char *p_blob;
8533     int n_bytes;
8534     int invalid = 0;
8535     int gpkg_amphibious = 0;
8536     int gpkg_mode = 0;
8537     int tiny_point = 0;
8538     sqlite3_context *context = (sqlite3_context *) xcontext;
8539     sqlite3_value **argv = (sqlite3_value **) xargv;
8540     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8541     struct splite_internal_cache *cache = sqlite3_user_data (context);
8542     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8543 
8544     if (cache != NULL)
8545       {
8546 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
8547 	  gpkg_mode = cache->gpkg_mode;
8548 	  tiny_point = cache->tinyPointEnabled;
8549       }
8550 
8551     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8552 	goto null_arg;
8553     if (sqlite3_value_type (argv[0]) == SQLITE_BLOB)
8554       {
8555 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
8556 	  n_bytes = sqlite3_value_bytes (argv[0]);
8557       }
8558     else
8559 	goto invalid_arg;
8560     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8561 	goto null_arg;
8562     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
8563 	topo_name = (const char *) sqlite3_value_text (argv[1]);
8564     else
8565 	goto invalid_arg;
8566     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
8567 	goto null_arg;
8568     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
8569       {
8570 	  int intval = sqlite3_value_int (argv[2]);
8571 	  dist = intval;
8572       }
8573     else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
8574 	dist = sqlite3_value_double (argv[2]);
8575     else
8576 	goto invalid_arg;
8577 
8578 /* attempting to get a Linestring Geometry */
8579     geom =
8580 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
8581 				     gpkg_amphibious);
8582     if (!geom)
8583 	goto invalid_arg;
8584     if (geom->FirstPoint != NULL)
8585 	invalid = 1;
8586     if (geom->FirstPolygon != NULL)
8587 	invalid = 1;
8588     if (geom->FirstLinestring != geom->LastLinestring
8589 	|| geom->FirstLinestring == NULL)
8590 	invalid = 1;
8591     if (invalid)
8592 	goto invalid_arg;
8593 
8594 /* attempting to get a Topology Accessor */
8595     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8596     if (accessor == NULL)
8597 	goto no_topo;
8598     gaiatopo_reset_last_error_msg (accessor);
8599     if (!check_matching_srid_dims (accessor, geom->Srid, geom->DimensionModel))
8600 	goto invalid_geom;
8601 
8602     result = gaiaTopoGeoSnapLinestringToSeed (accessor, geom, dist);
8603     if (result == NULL)
8604       {
8605 	  gaiaFreeGeomColl (geom);
8606 	  sqlite3_result_null (context);
8607 	  return;
8608       }
8609     gaiaToSpatiaLiteBlobWkbEx2 (result, &p_blob, &n_bytes, gpkg_mode,
8610 				tiny_point);
8611     gaiaFreeGeomColl (geom);
8612     gaiaFreeGeomColl (result);
8613     if (p_blob == NULL)
8614 	sqlite3_result_null (context);
8615     else
8616 	sqlite3_result_blob (context, p_blob, n_bytes, free);
8617     return;
8618 
8619   no_topo:
8620     if (geom != NULL)
8621 	gaiaFreeGeomColl (geom);
8622     if (result != NULL)
8623 	gaiaFreeGeomColl (result);
8624     msg = "SQL/MM Spatial exception - invalid topology name.";
8625     gaiatopo_set_last_error_msg (accessor, msg);
8626     sqlite3_result_error (context, msg, -1);
8627     return;
8628 
8629   null_arg:
8630     if (geom != NULL)
8631 	gaiaFreeGeomColl (geom);
8632     if (result != NULL)
8633 	gaiaFreeGeomColl (result);
8634     msg = "SQL/MM Spatial exception - null argument.";
8635     gaiatopo_set_last_error_msg (accessor, msg);
8636     sqlite3_result_error (context, msg, -1);
8637     return;
8638 
8639   invalid_arg:
8640     if (geom != NULL)
8641 	gaiaFreeGeomColl (geom);
8642     if (result != NULL)
8643 	gaiaFreeGeomColl (result);
8644     msg = "SQL/MM Spatial exception - invalid argument.";
8645     gaiatopo_set_last_error_msg (accessor, msg);
8646     sqlite3_result_error (context, msg, -1);
8647     return;
8648 
8649   invalid_geom:
8650     if (geom != NULL)
8651 	gaiaFreeGeomColl (geom);
8652     if (result != NULL)
8653 	gaiaFreeGeomColl (result);
8654     msg =
8655 	"SQL/MM Spatial exception - invalid Line (mismatching SRID or dimensions).";
8656     gaiatopo_set_last_error_msg (accessor, msg);
8657     sqlite3_result_error (context, msg, -1);
8658     return;
8659 }
8660 
8661 static int
topolayer_exists(GaiaTopologyAccessorPtr accessor,const char * topolayer_name)8662 topolayer_exists (GaiaTopologyAccessorPtr accessor, const char *topolayer_name)
8663 {
8664 /* checking if a TopoLayer is already defined */
8665     char *table;
8666     char *xtable;
8667     char *sql;
8668     int ret;
8669     int i;
8670     char **results;
8671     int rows;
8672     int columns;
8673     char *errMsg = NULL;
8674     int count = 0;
8675     struct gaia_topology *topo = (struct gaia_topology *) accessor;
8676     if (topo == NULL)
8677 	return 0;
8678 
8679     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
8680     xtable = gaiaDoubleQuotedSql (table);
8681     sqlite3_free (table);
8682     sql =
8683 	sqlite3_mprintf
8684 	("SELECT Count(*) FROM MAIN.\"%s\" WHERE topolayer_name = Lower(%Q)",
8685 	 xtable, topolayer_name);
8686     free (xtable);
8687     ret =
8688 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
8689 			   &errMsg);
8690     sqlite3_free (sql);
8691     if (ret != SQLITE_OK)
8692       {
8693 	  sqlite3_free (errMsg);
8694 	  return 0;
8695       }
8696     for (i = 1; i <= rows; i++)
8697       {
8698 	  count = atoi (results[(i * columns) + 0]);
8699       }
8700     sqlite3_free_table (results);
8701 
8702     if (count == 0)
8703 	return 0;
8704     return 1;
8705 }
8706 
8707 static int
check_view(struct gaia_topology * topo,const char * db_prefix,const char * table,const char * column)8708 check_view (struct gaia_topology *topo, const char *db_prefix,
8709 	    const char *table, const char *column)
8710 {
8711 /* checking a candidate View (or unregistered Table) for valid Geoms */
8712     char *sql;
8713     char *xcolumn;
8714     char *xprefix;
8715     char *xtable;
8716     int ret;
8717     sqlite3_stmt *stmt = NULL;
8718     int nulls = 0;
8719     int others = 0;
8720     int geoms = 0;
8721     int wrong_srids = 0;
8722 
8723     xcolumn = gaiaDoubleQuotedSql (column);
8724     xprefix = gaiaDoubleQuotedSql (db_prefix);
8725     xtable = gaiaDoubleQuotedSql (table);
8726     sql =
8727 	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\".\"%s\"", xcolumn, xprefix,
8728 			 xtable);
8729     free (xcolumn);
8730     free (xprefix);
8731     free (xtable);
8732     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
8733     sqlite3_free (sql);
8734     if (ret != SQLITE_OK)
8735       {
8736 	  char *msg =
8737 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
8738 			       sqlite3_errmsg (topo->db_handle));
8739 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
8740 	  sqlite3_free (msg);
8741 	  goto error;
8742       }
8743 
8744     sqlite3_reset (stmt);
8745     sqlite3_clear_bindings (stmt);
8746     while (1)
8747       {
8748 	  /* scrolling the result set rows */
8749 	  ret = sqlite3_step (stmt);
8750 	  if (ret == SQLITE_DONE)
8751 	      break;		/* end of result set */
8752 	  if (ret == SQLITE_ROW)
8753 	    {
8754 		gaiaGeomCollPtr geom = NULL;
8755 		const unsigned char *blob;
8756 		int blob_sz;
8757 		if (sqlite3_column_type (stmt, 0) == SQLITE_NULL)
8758 		    nulls++;
8759 		else if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
8760 		  {
8761 		      blob = sqlite3_column_blob (stmt, 0);
8762 		      blob_sz = sqlite3_column_bytes (stmt, 0);
8763 		      geom = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
8764 		      if (geom)
8765 			{
8766 			    if (geom->Srid != topo->srid)
8767 				wrong_srids++;
8768 			    gaiaFreeGeomColl (geom);
8769 			    geoms++;
8770 			}
8771 		      else
8772 			  others++;
8773 		  }
8774 		else
8775 		    others++;
8776 	    }
8777 	  else
8778 	    {
8779 		char *msg =
8780 		    sqlite3_mprintf ("TopoGeo_CreateTopoLayer step error: %s",
8781 				     sqlite3_errmsg (topo->db_handle));
8782 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
8783 					     msg);
8784 		sqlite3_free (msg);
8785 		goto error;
8786 	    }
8787       }
8788 
8789     sqlite3_finalize (stmt);
8790     if (geoms == 0)
8791 	return 0;
8792     if (others != 0)
8793 	return 0;
8794     if (wrong_srids != 0)
8795 	return 0;
8796     return 1;
8797 
8798   error:
8799     if (stmt != NULL)
8800 	sqlite3_finalize (stmt);
8801     return 0;
8802 }
8803 
8804 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_CreateTopoLayer(const void * xcontext,int argc,const void * xargv)8805 fnctaux_TopoGeo_CreateTopoLayer (const void *xcontext, int argc,
8806 				 const void *xargv)
8807 {
8808 /* SQL function:
8809 / TopoGeo_CreateTopoLayer ( text topology-name, text db-prefix, text ref_table,
8810 /                           text ref_column, text topolayer_name )
8811 / TopoGeo_CreateTopoLayer ( text topology-name, text db-prefix, text ref_table,
8812 /                           text ref_column, text topolayer_name,
8813 /                           boolean is_view )
8814 /
8815 / returns: 1 on success
8816 / raises an exception on failure
8817 */
8818     const char *msg;
8819     int ret;
8820     const char *topo_name;
8821     const char *db_prefix;
8822     const char *ref_table;
8823     const char *ref_column;
8824     const char *topolayer_name;
8825     int is_view = 0;
8826     char *xreftable = NULL;
8827     char *xrefcolumn = NULL;
8828     int srid;
8829     int family;
8830     GaiaTopologyAccessorPtr accessor = NULL;
8831     sqlite3_context *context = (sqlite3_context *) xcontext;
8832     sqlite3_value **argv = (sqlite3_value **) xargv;
8833     sqlite3 *sqlite = sqlite3_context_db_handle (context);
8834     struct splite_internal_cache *cache = sqlite3_user_data (context);
8835     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
8836     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
8837 	goto null_arg;
8838     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
8839 	topo_name = (const char *) sqlite3_value_text (argv[0]);
8840     else
8841 	goto invalid_arg;
8842     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
8843 	db_prefix = "main";
8844     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
8845 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
8846     else
8847 	goto invalid_arg;
8848     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
8849 	ref_table = (const char *) sqlite3_value_text (argv[2]);
8850     else
8851 	goto invalid_arg;
8852     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
8853 	ref_column = NULL;
8854     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
8855 	ref_column = (const char *) sqlite3_value_text (argv[3]);
8856     else
8857 	goto invalid_arg;
8858     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
8859 	goto null_arg;
8860     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
8861 	topolayer_name = (const char *) sqlite3_value_text (argv[4]);
8862     else
8863 	goto invalid_arg;
8864     if (argc >= 6)
8865       {
8866 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
8867 	      goto null_arg;
8868 	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
8869 	      is_view = sqlite3_value_int (argv[5]);
8870 	  else
8871 	      goto invalid_arg;
8872       }
8873 
8874 /* attempting to get a Topology Accessor */
8875     accessor = gaiaGetTopology (sqlite, cache, topo_name);
8876     if (accessor == NULL)
8877 	goto no_topo;
8878     gaiatopo_reset_last_error_msg (accessor);
8879 
8880     if (is_view)
8881       {
8882 	  /* checking a View (or an unregistered geo-table) */
8883 	  struct gaia_topology *topo = (struct gaia_topology *) accessor;
8884 	  if (ref_column == NULL)
8885 	      goto null_view_geom;
8886 	  if (!check_view (topo, db_prefix, ref_table, ref_column))
8887 	      goto invalid_view;
8888 	  xreftable = malloc (strlen (ref_table) + 1);
8889 	  strcpy (xreftable, ref_table);
8890 	  xrefcolumn = malloc (strlen (ref_column) + 1);
8891 	  strcpy (xrefcolumn, ref_column);
8892       }
8893     else
8894       {
8895 	  /* checking the reference GeoTable */
8896 	  if (!gaia_check_reference_geo_table
8897 	      (sqlite, db_prefix, ref_table, ref_column, &xreftable,
8898 	       &xrefcolumn, &srid, &family))
8899 	      goto no_reference;
8900 	  if (!check_matching_srid (accessor, srid))
8901 	      goto invalid_geom;
8902       }
8903 
8904 /* checking the output TopoLayer */
8905     if (topolayer_exists (accessor, topolayer_name))
8906 	goto err_output;
8907 
8908     start_topo_savepoint (sqlite, cache);
8909     ret =
8910 	gaiaTopoGeo_CreateTopoLayer (accessor, db_prefix, xreftable, xrefcolumn,
8911 				     topolayer_name);
8912     if (!ret)
8913 	rollback_topo_savepoint (sqlite, cache);
8914     else
8915 	release_topo_savepoint (sqlite, cache);
8916     free (xreftable);
8917     free (xrefcolumn);
8918     if (!ret)
8919       {
8920 	  msg = gaiaGetRtTopoErrorMsg (cache);
8921 	  gaiatopo_set_last_error_msg (accessor, msg);
8922 	  sqlite3_result_error (context, msg, -1);
8923 	  return;
8924       }
8925     sqlite3_result_int (context, 1);
8926     return;
8927 
8928   no_topo:
8929     if (xreftable != NULL)
8930 	free (xreftable);
8931     if (xrefcolumn != NULL)
8932 	free (xrefcolumn);
8933     msg = "SQL/MM Spatial exception - invalid topology name.";
8934     gaiatopo_set_last_error_msg (accessor, msg);
8935     sqlite3_result_error (context, msg, -1);
8936     return;
8937 
8938   no_reference:
8939     if (xreftable != NULL)
8940 	free (xreftable);
8941     if (xrefcolumn != NULL)
8942 	free (xrefcolumn);
8943     msg = "TopoGeo_CreateTopoLayer: invalid reference GeoTable.";
8944     gaiatopo_set_last_error_msg (accessor, msg);
8945     sqlite3_result_error (context, msg, -1);
8946     return;
8947 
8948   null_view_geom:
8949     if (xreftable != NULL)
8950 	free (xreftable);
8951     if (xrefcolumn != NULL)
8952 	free (xrefcolumn);
8953     msg =
8954 	"TopoGeo_CreateTopoLayer: IsView requires an explicit Geometry column-name.";
8955     gaiatopo_set_last_error_msg (accessor, msg);
8956     sqlite3_result_error (context, msg, -1);
8957     return;
8958 
8959   invalid_view:
8960     if (xreftable != NULL)
8961 	free (xreftable);
8962     if (xrefcolumn != NULL)
8963 	free (xrefcolumn);
8964     msg = "TopoGeo_CreateTopoLayer: invalid reference View (invalid Geometry).";
8965     gaiatopo_set_last_error_msg (accessor, msg);
8966     sqlite3_result_error (context, msg, -1);
8967     return;
8968 
8969   err_output:
8970     if (xreftable != NULL)
8971 	free (xreftable);
8972     if (xrefcolumn != NULL)
8973 	free (xrefcolumn);
8974     msg =
8975 	"TopoGeo_CreateTopoLayer: a TopoLayer of the same name already exists.";
8976     gaiatopo_set_last_error_msg (accessor, msg);
8977     sqlite3_result_error (context, msg, -1);
8978     return;
8979 
8980   null_arg:
8981     if (xreftable != NULL)
8982 	free (xreftable);
8983     if (xrefcolumn != NULL)
8984 	free (xrefcolumn);
8985     msg = "SQL/MM Spatial exception - null argument.";
8986     gaiatopo_set_last_error_msg (accessor, msg);
8987     sqlite3_result_error (context, msg, -1);
8988     return;
8989 
8990   invalid_arg:
8991     if (xreftable != NULL)
8992 	free (xreftable);
8993     if (xrefcolumn != NULL)
8994 	free (xrefcolumn);
8995     msg = "SQL/MM Spatial exception - invalid argument.";
8996     gaiatopo_set_last_error_msg (accessor, msg);
8997     sqlite3_result_error (context, msg, -1);
8998     return;
8999 
9000   invalid_geom:
9001     if (xreftable != NULL)
9002 	free (xreftable);
9003     if (xrefcolumn != NULL)
9004 	free (xrefcolumn);
9005     msg =
9006 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
9007     gaiatopo_set_last_error_msg (accessor, msg);
9008     sqlite3_result_error (context, msg, -1);
9009     return;
9010 }
9011 
9012 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_InitTopoLayer(const void * xcontext,int argc,const void * xargv)9013 fnctaux_TopoGeo_InitTopoLayer (const void *xcontext, int argc,
9014 			       const void *xargv)
9015 {
9016 /* SQL function:
9017 / TopoGeo_InitTopoLayer ( text topology-name, text db-prefix, text ref_table,
9018 /                         text topolayer_name )
9019 /
9020 / returns: 1 on success
9021 / raises an exception on failure
9022 */
9023     const char *msg;
9024     int ret;
9025     const char *topo_name;
9026     const char *db_prefix;
9027     const char *ref_table;
9028     const char *topolayer_name;
9029     GaiaTopologyAccessorPtr accessor = NULL;
9030     sqlite3_context *context = (sqlite3_context *) xcontext;
9031     sqlite3_value **argv = (sqlite3_value **) xargv;
9032     sqlite3 *sqlite = sqlite3_context_db_handle (context);
9033     struct splite_internal_cache *cache = sqlite3_user_data (context);
9034     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
9035     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
9036 	goto null_arg;
9037     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
9038 	topo_name = (const char *) sqlite3_value_text (argv[0]);
9039     else
9040 	goto invalid_arg;
9041     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
9042 	db_prefix = "main";
9043     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
9044 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
9045     else
9046 	goto invalid_arg;
9047     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
9048 	ref_table = (const char *) sqlite3_value_text (argv[2]);
9049     else
9050 	goto invalid_arg;
9051     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
9052 	goto null_arg;
9053     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
9054 	topolayer_name = (const char *) sqlite3_value_text (argv[3]);
9055     else
9056 	goto invalid_arg;
9057 
9058 /* attempting to get a Topology Accessor */
9059     accessor = gaiaGetTopology (sqlite, cache, topo_name);
9060     if (accessor == NULL)
9061 	goto no_topo;
9062     gaiatopo_reset_last_error_msg (accessor);
9063 
9064 /* checking the reference Table */
9065     if (!check_reference_table (sqlite, db_prefix, ref_table))
9066 	goto no_reference;
9067 
9068 /* checking the output TopoLayer */
9069     if (topolayer_exists (accessor, topolayer_name))
9070 	goto err_output;
9071 
9072     start_topo_savepoint (sqlite, cache);
9073     ret =
9074 	gaiaTopoGeo_InitTopoLayer (accessor, db_prefix, ref_table,
9075 				   topolayer_name);
9076     if (!ret)
9077 	rollback_topo_savepoint (sqlite, cache);
9078     else
9079 	release_topo_savepoint (sqlite, cache);
9080     if (!ret)
9081       {
9082 	  msg = gaiaGetRtTopoErrorMsg (cache);
9083 	  gaiatopo_set_last_error_msg (accessor, msg);
9084 	  sqlite3_result_error (context, msg, -1);
9085 	  return;
9086       }
9087     sqlite3_result_int (context, 1);
9088     return;
9089 
9090   no_topo:
9091     msg = "SQL/MM Spatial exception - invalid topology name.";
9092     gaiatopo_set_last_error_msg (accessor, msg);
9093     sqlite3_result_error (context, msg, -1);
9094     return;
9095 
9096   no_reference:
9097     msg = "TopoGeo_InitTopoLayer: invalid reference Table.";
9098     gaiatopo_set_last_error_msg (accessor, msg);
9099     sqlite3_result_error (context, msg, -1);
9100     return;
9101 
9102   err_output:
9103     msg = "TopoGeo_InitTopoLayer: a TopoLayer of the same name already exists.";
9104     gaiatopo_set_last_error_msg (accessor, msg);
9105     sqlite3_result_error (context, msg, -1);
9106     return;
9107 
9108   null_arg:
9109     msg = "SQL/MM Spatial exception - null argument.";
9110     gaiatopo_set_last_error_msg (accessor, msg);
9111     sqlite3_result_error (context, msg, -1);
9112     return;
9113 
9114   invalid_arg:
9115     msg = "SQL/MM Spatial exception - invalid argument.";
9116     gaiatopo_set_last_error_msg (accessor, msg);
9117     sqlite3_result_error (context, msg, -1);
9118     return;
9119 }
9120 
9121 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_RemoveTopoLayer(const void * xcontext,int argc,const void * xargv)9122 fnctaux_TopoGeo_RemoveTopoLayer (const void *xcontext, int argc,
9123 				 const void *xargv)
9124 {
9125 /* SQL function:
9126 / TopoGeo_RemoveTopoLayer ( text topology-name, text topolayer_name )
9127 /
9128 / returns: 1 on success
9129 / raises an exception on failure
9130 */
9131     const char *msg;
9132     int ret;
9133     const char *topo_name;
9134     const char *topolayer_name;
9135     GaiaTopologyAccessorPtr accessor = NULL;
9136     sqlite3_context *context = (sqlite3_context *) xcontext;
9137     sqlite3_value **argv = (sqlite3_value **) xargv;
9138     sqlite3 *sqlite = sqlite3_context_db_handle (context);
9139     struct splite_internal_cache *cache = sqlite3_user_data (context);
9140     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
9141     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
9142 	goto null_arg;
9143     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
9144 	topo_name = (const char *) sqlite3_value_text (argv[0]);
9145     else
9146 	goto invalid_arg;
9147     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
9148 	goto null_arg;
9149     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
9150 	topolayer_name = (const char *) sqlite3_value_text (argv[1]);
9151     else
9152 	goto invalid_arg;
9153 
9154 /* attempting to get a Topology Accessor */
9155     accessor = gaiaGetTopology (sqlite, cache, topo_name);
9156     if (accessor == NULL)
9157 	goto no_topo;
9158     gaiatopo_reset_last_error_msg (accessor);
9159 
9160 /* checking the TopoLayer */
9161     if (!topolayer_exists (accessor, topolayer_name))
9162 	goto err_topolayer;
9163 
9164     start_topo_savepoint (sqlite, cache);
9165     ret = gaiaTopoGeo_RemoveTopoLayer (accessor, topolayer_name);
9166     if (!ret)
9167 	rollback_topo_savepoint (sqlite, cache);
9168     else
9169 	release_topo_savepoint (sqlite, cache);
9170     if (!ret)
9171       {
9172 	  const char *msg = gaiaGetRtTopoErrorMsg (cache);
9173 	  gaiatopo_set_last_error_msg (accessor, msg);
9174 	  sqlite3_result_error (context, msg, -1);
9175 	  return;
9176       }
9177     sqlite3_result_int (context, 1);
9178     return;
9179 
9180   no_topo:
9181     msg = "SQL/MM Spatial exception - invalid topology name.";
9182     gaiatopo_set_last_error_msg (accessor, msg);
9183     sqlite3_result_error (context, msg, -1);
9184     return;
9185 
9186   err_topolayer:
9187     msg = "TopoGeo_RemoveTopoLayer: not existing TopoLayer.";
9188     gaiatopo_set_last_error_msg (accessor, msg);
9189     sqlite3_result_error (context, msg, -1);
9190     return;
9191 
9192   null_arg:
9193     msg = "SQL/MM Spatial exception - null argument.";
9194     gaiatopo_set_last_error_msg (accessor, msg);
9195     sqlite3_result_error (context, msg, -1);
9196     return;
9197 
9198   invalid_arg:
9199     msg = "SQL/MM Spatial exception - invalid argument.";
9200     gaiatopo_set_last_error_msg (accessor, msg);
9201     sqlite3_result_error (context, msg, -1);
9202     return;
9203 }
9204 
9205 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_ExportTopoLayer(const void * xcontext,int argc,const void * xargv)9206 fnctaux_TopoGeo_ExportTopoLayer (const void *xcontext, int argc,
9207 				 const void *xargv)
9208 {
9209 /* SQL function:
9210 / TopoGeo_ExportTopoLayer ( text topology-name, text topolayer_name,
9211 /                           text out_table )
9212 / TopoGeo_ExportTopoLayer ( text topology-name, text topolayer_name,
9213 /                           text out_table, integer with-spatial-index )
9214 / TopoGeo_ExportTopoLayer ( text topology-name, text topolayer_name,
9215 /                           text out_table, integer with-spatial-index,
9216 /                           integer create-only )
9217 /
9218 / returns: 1 on success
9219 / raises an exception on failure
9220 */
9221     const char *msg;
9222     int ret;
9223     const char *topo_name;
9224     const char *topolayer_name;
9225     const char *out_table;
9226     int with_spatial_index = 0;
9227     int create_only = 0;
9228     GaiaTopologyAccessorPtr accessor = NULL;
9229     sqlite3_context *context = (sqlite3_context *) xcontext;
9230     sqlite3_value **argv = (sqlite3_value **) xargv;
9231     sqlite3 *sqlite = sqlite3_context_db_handle (context);
9232     struct splite_internal_cache *cache = sqlite3_user_data (context);
9233     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
9234     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
9235 	goto null_arg;
9236     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
9237 	topo_name = (const char *) sqlite3_value_text (argv[0]);
9238     else
9239 	goto invalid_arg;
9240     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
9241 	goto null_arg;
9242     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
9243 	topolayer_name = (const char *) sqlite3_value_text (argv[1]);
9244     else
9245 	goto invalid_arg;
9246     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
9247 	goto null_arg;
9248     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
9249 	out_table = (const char *) sqlite3_value_text (argv[2]);
9250     else
9251 	goto invalid_arg;
9252     if (argc >= 4)
9253       {
9254 	  if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
9255 	      goto null_arg;
9256 	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
9257 	      with_spatial_index = sqlite3_value_int (argv[3]);
9258 	  else
9259 	      goto invalid_arg;
9260       }
9261     if (argc >= 5)
9262       {
9263 	  if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
9264 	      goto null_arg;
9265 	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
9266 	      create_only = sqlite3_value_int (argv[4]);
9267 	  else
9268 	      goto invalid_arg;
9269       }
9270 
9271 /* attempting to get a Topology Accessor */
9272     accessor = gaiaGetTopology (sqlite, cache, topo_name);
9273     if (accessor == NULL)
9274 	goto no_topo;
9275     gaiatopo_reset_last_error_msg (accessor);
9276 
9277 /* checking the input TopoLayer */
9278     if (!topolayer_exists (accessor, topolayer_name))
9279 	goto err_topolayer;
9280 
9281 /* checking the output GeoTable */
9282     if (!check_output_geo_table (sqlite, out_table))
9283 	goto invalid_output;
9284 
9285     start_topo_savepoint (sqlite, cache);
9286     ret =
9287 	gaiaTopoGeo_ExportTopoLayer (accessor, topolayer_name, out_table,
9288 				     with_spatial_index, create_only);
9289     if (!ret)
9290 	rollback_topo_savepoint (sqlite, cache);
9291     else
9292 	release_topo_savepoint (sqlite, cache);
9293     if (!ret)
9294       {
9295 	  msg = gaiaGetRtTopoErrorMsg (cache);
9296 	  gaiatopo_set_last_error_msg (accessor, msg);
9297 	  sqlite3_result_error (context, msg, -1);
9298 	  return;
9299       }
9300     sqlite3_result_int (context, 1);
9301     return;
9302 
9303   no_topo:
9304     msg = "SQL/MM Spatial exception - invalid topology name.";
9305     gaiatopo_set_last_error_msg (accessor, msg);
9306     sqlite3_result_error (context, msg, -1);
9307     return;
9308 
9309   invalid_output:
9310     msg = "TopoGeo_ExportTopoLayer: the output GeoTable already exists.";
9311     gaiatopo_set_last_error_msg (accessor, msg);
9312     sqlite3_result_error (context, msg, -1);
9313     return;
9314 
9315   err_topolayer:
9316     sqlite3_result_error (context,
9317 			  "TopoGeo_ExportTopoLayer: not existing TopoLayer.",
9318 			  -1);
9319     return;
9320 
9321   null_arg:
9322     msg = "SQL/MM Spatial exception - null argument.";
9323     gaiatopo_set_last_error_msg (accessor, msg);
9324     sqlite3_result_error (context, msg, -1);
9325     return;
9326 
9327   invalid_arg:
9328     msg = "SQL/MM Spatial exception - invalid argument.";
9329     gaiatopo_set_last_error_msg (accessor, msg);
9330     sqlite3_result_error (context, msg, -1);
9331     return;
9332 }
9333 
9334 SPATIALITE_PRIVATE void
fnctaux_TopoGeo_InsertFeatureFromTopoLayer(const void * xcontext,int argc,const void * xargv)9335 fnctaux_TopoGeo_InsertFeatureFromTopoLayer (const void *xcontext, int argc,
9336 					    const void *xargv)
9337 {
9338 /* SQL function:
9339 / TopoGeo_InsertFeatureFromTopoLayer ( text topology-name,
9340 /                                      text topolayer_name,
9341 /                                      text out_table, integer fid )
9342 /
9343 / returns: 1 on success
9344 / raises an exception on failure
9345 */
9346     const char *msg;
9347     int ret;
9348     const char *topo_name;
9349     const char *topolayer_name;
9350     const char *out_table;
9351     sqlite3_int64 fid;
9352     GaiaTopologyAccessorPtr accessor = NULL;
9353     sqlite3_context *context = (sqlite3_context *) xcontext;
9354     sqlite3_value **argv = (sqlite3_value **) xargv;
9355     sqlite3 *sqlite = sqlite3_context_db_handle (context);
9356     struct splite_internal_cache *cache = sqlite3_user_data (context);
9357     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
9358     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
9359 	goto null_arg;
9360     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
9361 	topo_name = (const char *) sqlite3_value_text (argv[0]);
9362     else
9363 	goto invalid_arg;
9364     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
9365 	goto null_arg;
9366     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
9367 	topolayer_name = (const char *) sqlite3_value_text (argv[1]);
9368     else
9369 	goto invalid_arg;
9370     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
9371 	goto null_arg;
9372     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
9373 	out_table = (const char *) sqlite3_value_text (argv[2]);
9374     else
9375 	goto invalid_arg;
9376     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
9377 	goto null_arg;
9378     else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
9379 	fid = sqlite3_value_int64 (argv[3]);
9380     else
9381 	goto invalid_arg;
9382 
9383 /* attempting to get a Topology Accessor */
9384     accessor = gaiaGetTopology (sqlite, cache, topo_name);
9385     if (accessor == NULL)
9386 	goto no_topo;
9387     gaiatopo_reset_last_error_msg (accessor);
9388 
9389 /* checking the input TopoLayer */
9390     if (!topolayer_exists (accessor, topolayer_name))
9391 	goto err_topolayer;
9392 
9393 /* checking the output GeoTable */
9394     if (check_output_geo_table (sqlite, out_table))
9395 	goto invalid_output;
9396 
9397     start_topo_savepoint (sqlite, cache);
9398     ret =
9399 	gaiaTopoGeo_InsertFeatureFromTopoLayer (accessor, topolayer_name,
9400 						out_table, fid);
9401     if (!ret)
9402 	rollback_topo_savepoint (sqlite, cache);
9403     else
9404 	release_topo_savepoint (sqlite, cache);
9405     if (!ret)
9406       {
9407 	  msg = gaiaGetRtTopoErrorMsg (cache);
9408 	  gaiatopo_set_last_error_msg (accessor, msg);
9409 	  sqlite3_result_error (context, msg, -1);
9410 	  return;
9411       }
9412     sqlite3_result_int (context, 1);
9413     return;
9414 
9415   no_topo:
9416     msg = "SQL/MM Spatial exception - invalid topology name.";
9417     gaiatopo_set_last_error_msg (accessor, msg);
9418     sqlite3_result_error (context, msg, -1);
9419     return;
9420 
9421   invalid_output:
9422     msg =
9423 	"TopoGeo_InsertFeatureFromTopoLayer: the output GeoTable does not exists.";
9424     gaiatopo_set_last_error_msg (accessor, msg);
9425     sqlite3_result_error (context, msg, -1);
9426     return;
9427 
9428   err_topolayer:
9429     msg = "TopoGeo_InsertFeatureFromTopoLayer: non-existing TopoLayer.";
9430     gaiatopo_set_last_error_msg (accessor, msg);
9431     sqlite3_result_error (context, msg, -1);
9432     return;
9433 
9434   null_arg:
9435     msg = "SQL/MM Spatial exception - null argument.";
9436     gaiatopo_set_last_error_msg (accessor, msg);
9437     sqlite3_result_error (context, msg, -1);
9438     return;
9439 
9440   invalid_arg:
9441     msg = "SQL/MM Spatial exception - invalid argument.";
9442     gaiatopo_set_last_error_msg (accessor, msg);
9443     sqlite3_result_error (context, msg, -1);
9444     return;
9445 }
9446 
9447 #endif /* end RTTOPO conditionals */
9448