1 /*
2 
3  gaia_network.c -- implementation of Topology-Network 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 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 
50 #if defined(_WIN32) && !defined(__MINGW32__)
51 #include "config-msvc.h"
52 #else
53 #include "config.h"
54 #endif
55 
56 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
57 
58 #include <spatialite/sqlite.h>
59 #include <spatialite/debug.h>
60 #include <spatialite/gaiageo.h>
61 #include <spatialite/gaia_network.h>
62 #include <spatialite/gaia_topology.h>
63 #include <spatialite/gaiaaux.h>
64 
65 #include <spatialite.h>
66 #include <spatialite_private.h>
67 
68 #include <librttopo.h>
69 #include <lwn_network.h>
70 
71 #include "network_private.h"
72 #include "topology_private.h"
73 
74 #ifdef _WIN32
75 #define strcasecmp	_stricmp
76 #endif /* not WIN32 */
77 
78 #define GAIA_UNUSED() if (argc || argv) argc = argc;
79 
80 static struct splite_savepoint *
push_net_savepoint(struct splite_internal_cache * cache)81 push_net_savepoint (struct splite_internal_cache *cache)
82 {
83 /* adding a new SavePoint to the Network stack */
84     struct splite_savepoint *p_svpt = malloc (sizeof (struct splite_savepoint));
85     p_svpt->savepoint_name = NULL;
86     p_svpt->prev = cache->last_net_svpt;
87     p_svpt->next = NULL;
88     if (cache->first_net_svpt == NULL)
89 	cache->first_net_svpt = p_svpt;
90     if (cache->last_net_svpt != NULL)
91 	cache->last_net_svpt->next = p_svpt;
92     cache->last_net_svpt = p_svpt;
93     return p_svpt;
94 }
95 
96 static void
pop_net_savepoint(struct splite_internal_cache * cache)97 pop_net_savepoint (struct splite_internal_cache *cache)
98 {
99 /* removing a SavePoint from the Network stack */
100     struct splite_savepoint *p_svpt = cache->last_net_svpt;
101     if (p_svpt->prev != NULL)
102 	p_svpt->prev->next = NULL;
103     cache->last_net_svpt = p_svpt->prev;
104     if (cache->first_net_svpt == p_svpt)
105 	cache->first_net_svpt = NULL;
106     if (p_svpt->savepoint_name != NULL)
107 	sqlite3_free (p_svpt->savepoint_name);
108     free (p_svpt);
109 }
110 
111 SPATIALITE_PRIVATE void
start_net_savepoint(const void * handle,const void * data)112 start_net_savepoint (const void *handle, const void *data)
113 {
114 /* starting a new SAVEPOINT */
115     char *sql;
116     int ret;
117     char *err_msg;
118     struct splite_savepoint *p_svpt;
119     sqlite3 *sqlite = (sqlite3 *) handle;
120     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
121     if (sqlite == NULL || cache == NULL)
122 	return;
123 
124 /* creating an unique SavePoint name */
125     p_svpt = push_net_savepoint (cache);
126     p_svpt->savepoint_name =
127 	sqlite3_mprintf ("netsvpt%04x", cache->next_network_savepoint);
128     if (cache->next_network_savepoint >= 0xffffffffu)
129 	cache->next_network_savepoint = 0;
130     else
131 	cache->next_network_savepoint += 1;
132 
133 /* starting a SavePoint */
134     sql = sqlite3_mprintf ("SAVEPOINT %s", p_svpt->savepoint_name);
135     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
136     if (ret != SQLITE_OK)
137       {
138 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
139 	  sqlite3_free (err_msg);
140       }
141     sqlite3_free (sql);
142 }
143 
144 SPATIALITE_PRIVATE void
release_net_savepoint(const void * handle,const void * data)145 release_net_savepoint (const void *handle, const void *data)
146 {
147 /* releasing the current SAVEPOINT (if any) */
148     char *sql;
149     int ret;
150     char *err_msg;
151     struct splite_savepoint *p_svpt;
152     sqlite3 *sqlite = (sqlite3 *) handle;
153     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
154     if (sqlite == NULL || cache == NULL)
155 	return;
156     p_svpt = cache->last_net_svpt;
157     if (p_svpt == NULL)
158 	return;
159     if (p_svpt->savepoint_name == NULL)
160 	return;
161 
162 /* releasing the current SavePoint */
163     sql = sqlite3_mprintf ("RELEASE SAVEPOINT %s", p_svpt->savepoint_name);
164     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
165     if (ret != SQLITE_OK)
166       {
167 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
168 	  sqlite3_free (err_msg);
169       }
170     sqlite3_free (sql);
171     pop_net_savepoint (cache);
172 }
173 
174 SPATIALITE_PRIVATE void
rollback_net_savepoint(const void * handle,const void * data)175 rollback_net_savepoint (const void *handle, const void *data)
176 {
177 /* rolling back the current SAVEPOINT (if any) */
178     char *sql;
179     int ret;
180     char *err_msg;
181     struct splite_savepoint *p_svpt;
182     sqlite3 *sqlite = (sqlite3 *) handle;
183     struct splite_internal_cache *cache = (struct splite_internal_cache *) data;
184     if (sqlite == NULL || cache == NULL)
185 	return;
186     p_svpt = cache->last_net_svpt;
187     if (p_svpt == NULL)
188 	return;
189     if (p_svpt->savepoint_name == NULL)
190 	return;
191 
192 /* rolling back the current SavePoint */
193     sql = sqlite3_mprintf ("ROLLBACK TO SAVEPOINT %s", p_svpt->savepoint_name);
194     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
195     if (ret != SQLITE_OK)
196       {
197 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
198 	  sqlite3_free (err_msg);
199       }
200     sqlite3_free (sql);
201 /* releasing the current SavePoint */
202     sql = sqlite3_mprintf ("RELEASE SAVEPOINT %s", p_svpt->savepoint_name);
203     ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
204     if (ret != SQLITE_OK)
205       {
206 	  spatialite_e ("%s - error: %s\n", sql, err_msg);
207 	  sqlite3_free (err_msg);
208       }
209     sqlite3_free (sql);
210     pop_net_savepoint (cache);
211 }
212 
213 SPATIALITE_PRIVATE void
fnctaux_GetLastNetworkException(const void * xcontext,int argc,const void * xargv)214 fnctaux_GetLastNetworkException (const void *xcontext, int argc,
215 				 const void *xargv)
216 {
217 /* SQL function:
218 / GetLastNetworkException  ( text network-name )
219 /
220 / returns: the more recent exception raised by given Topology-Network
221 / NULL on invalid args (or when there is no pending exception)
222 */
223     const char *network_name;
224     GaiaNetworkAccessorPtr accessor;
225     sqlite3_context *context = (sqlite3_context *) xcontext;
226     sqlite3_value **argv = (sqlite3_value **) xargv;
227     sqlite3 *sqlite = sqlite3_context_db_handle (context);
228     struct splite_internal_cache *cache = sqlite3_user_data (context);
229     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
230     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
231 	network_name = (const char *) sqlite3_value_text (argv[0]);
232     else
233       {
234 	  sqlite3_result_null (context);
235 	  return;
236       }
237 
238 /* attempting to get a Network Accessor */
239     accessor = gaiaGetNetwork (sqlite, cache, network_name);
240     if (accessor == NULL)
241       {
242 	  sqlite3_result_null (context);
243 	  return;
244       }
245 
246     sqlite3_result_text (context, gaianet_get_last_exception (accessor), -1,
247 			 SQLITE_STATIC);
248 }
249 
250 SPATIALITE_PRIVATE void
fnctaux_CreateNetwork(const void * xcontext,int argc,const void * xargv)251 fnctaux_CreateNetwork (const void *xcontext, int argc, const void *xargv)
252 {
253 /* SQL function:
254 / ST_InitTopoNet ( text network-name )
255 / CreateNetwork ( text network-name )
256 / CreateNetwork ( text network-name, bool spatial )
257 / CreateNetwork ( text network-name, bool spatial, int srid )
258 / CreateNetwork ( text network-name, bool spatial, int srid, bool hasZ )
259 / CreateNetwork ( text network-name, bool spatial, int srid, bool hasZ,
260 /                 bool allow_coincident )
261 /
262 / returns: 1 on success, 0 on failure
263 / -1 on invalid args
264 */
265     int ret;
266     const char *network_name;
267     int srid = -1;
268     int has_z = 0;
269     int spatial = 0;
270     int allow_coincident = 1;
271     sqlite3_context *context = (sqlite3_context *) xcontext;
272     sqlite3_value **argv = (sqlite3_value **) xargv;
273     sqlite3 *sqlite = sqlite3_context_db_handle (context);
274     struct splite_internal_cache *cache = sqlite3_user_data (context);
275     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
276     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
277 	network_name = (const char *) sqlite3_value_text (argv[0]);
278     else
279       {
280 	  sqlite3_result_int (context, -1);
281 	  return;
282       }
283     if (argc >= 2)
284       {
285 	  if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
286 	      ;
287 	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
288 	      spatial = sqlite3_value_int (argv[1]);
289 	  else
290 	    {
291 		sqlite3_result_int (context, -1);
292 		return;
293 	    }
294       }
295     if (argc >= 3)
296       {
297 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
298 	      ;
299 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
300 	      srid = sqlite3_value_int (argv[2]);
301 	  else
302 	    {
303 		sqlite3_result_int (context, -1);
304 		return;
305 	    }
306       }
307     if (argc >= 4)
308       {
309 	  if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
310 	      ;
311 	  else if (sqlite3_value_type (argv[3]) == SQLITE_INTEGER)
312 	      has_z = sqlite3_value_int (argv[3]);
313 	  else
314 	    {
315 		sqlite3_result_int (context, -1);
316 		return;
317 	    }
318       }
319     if (argc >= 5)
320       {
321 	  if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
322 	      ;
323 	  else if (sqlite3_value_type (argv[4]) == SQLITE_INTEGER)
324 	      allow_coincident = sqlite3_value_int (argv[4]);
325 	  else
326 	    {
327 		sqlite3_result_int (context, -1);
328 		return;
329 	    }
330       }
331 
332     start_net_savepoint (sqlite, cache);
333     ret =
334 	gaiaNetworkCreate (sqlite, network_name, spatial, srid, has_z,
335 			   allow_coincident);
336     if (!ret)
337 	rollback_net_savepoint (sqlite, cache);
338     else
339 	release_net_savepoint (sqlite, cache);
340     sqlite3_result_int (context, ret);
341 }
342 
343 SPATIALITE_PRIVATE void
fnctaux_DropNetwork(const void * xcontext,int argc,const void * xargv)344 fnctaux_DropNetwork (const void *xcontext, int argc, const void *xargv)
345 {
346 /* SQL function:
347 / DropNetwork ( text network-name )
348 /
349 / returns: 1 on success, 0 on failure
350 / -1 on invalid args
351 */
352     int ret;
353     const char *network_name;
354     sqlite3_context *context = (sqlite3_context *) xcontext;
355     sqlite3_value **argv = (sqlite3_value **) xargv;
356     sqlite3 *sqlite = sqlite3_context_db_handle (context);
357     struct splite_internal_cache *cache = sqlite3_user_data (context);
358     GaiaNetworkAccessorPtr accessor;
359     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
360     if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
361 	network_name = (const char *) sqlite3_value_text (argv[0]);
362     else
363       {
364 	  sqlite3_result_int (context, -1);
365 	  return;
366       }
367 
368     accessor = gaiaGetNetwork (sqlite, cache, network_name);
369     if (accessor != NULL)
370 	gaiaNetworkDestroy (accessor);
371 
372     start_net_savepoint (sqlite, cache);
373     ret = gaiaNetworkDrop (sqlite, network_name);
374     if (!ret)
375 	rollback_net_savepoint (sqlite, cache);
376     else
377 	release_net_savepoint (sqlite, cache);
378     sqlite3_result_int (context, ret);
379 }
380 
381 static int
check_matching_srid_dims(GaiaNetworkAccessorPtr accessor,int srid,int dims)382 check_matching_srid_dims (GaiaNetworkAccessorPtr accessor, int srid, int dims)
383 {
384 /* checking for matching SRID and DIMs */
385     struct gaia_network *net = (struct gaia_network *) accessor;
386     if (net->srid != srid)
387 	return 0;
388     if (net->has_z)
389       {
390 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
391 	      ;
392 	  else
393 	      return 0;
394       }
395     else
396       {
397 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
398 	      return 0;
399       }
400     return 1;
401 }
402 
403 SPATIALITE_PRIVATE void
fnctaux_AddIsoNetNode(const void * xcontext,int argc,const void * xargv)404 fnctaux_AddIsoNetNode (const void *xcontext, int argc, const void *xargv)
405 {
406 /* SQL function:
407 / ST_AddIsoNetNode ( text network-name, Geometry point )
408 /
409 / returns: the ID of the inserted Node on success
410 / raises an exception on failure
411 */
412     sqlite3_int64 ret;
413     const char *network_name;
414     unsigned char *p_blob;
415     int n_bytes;
416     gaiaGeomCollPtr point = NULL;
417     gaiaPointPtr pt = NULL;
418     int invalid = 0;
419     GaiaNetworkAccessorPtr accessor;
420     struct gaia_network *net;
421     int gpkg_amphibious = 0;
422     int gpkg_mode = 0;
423     sqlite3_context *context = (sqlite3_context *) xcontext;
424     sqlite3_value **argv = (sqlite3_value **) xargv;
425     sqlite3 *sqlite = sqlite3_context_db_handle (context);
426     struct splite_internal_cache *cache = sqlite3_user_data (context);
427     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
428     if (cache != NULL)
429       {
430 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
431 	  gpkg_mode = cache->gpkg_mode;
432       }
433     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
434 	goto null_arg;
435     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
436 	network_name = (const char *) sqlite3_value_text (argv[0]);
437     else
438 	goto invalid_arg;
439 
440 /* attempting to get a Network Accessor */
441     accessor = gaiaGetNetwork (sqlite, cache, network_name);
442     if (accessor == NULL)
443 	goto no_net;
444     net = (struct gaia_network *) accessor;
445 
446     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
447       {
448 	  if (net->spatial)
449 	      goto spatial_err;
450       }
451     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
452       {
453 	  if (net->spatial == 0)
454 	      goto logical_err;
455 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
456 	  n_bytes = sqlite3_value_bytes (argv[1]);
457 
458 	  /* attempting to get a Point Geometry */
459 	  point =
460 	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
461 					   gpkg_amphibious);
462 	  if (!point)
463 	      goto invalid_arg;
464 	  if (point->FirstLinestring != NULL)
465 	      invalid = 1;
466 	  if (point->FirstPolygon != NULL)
467 	      invalid = 1;
468 	  if (point->FirstPoint != point->LastPoint
469 	      || point->FirstPoint == NULL)
470 	      invalid = 1;
471 	  if (invalid)
472 	      goto invalid_arg;
473 
474 	  if (!check_matching_srid_dims
475 	      (accessor, point->Srid, point->DimensionModel))
476 	      goto invalid_geom;
477 	  pt = point->FirstPoint;
478       }
479     else
480 	goto invalid_arg;
481 
482     gaianet_reset_last_error_msg (accessor);
483     start_net_savepoint (sqlite, cache);
484     ret = gaiaAddIsoNetNode (accessor, pt);
485     if (ret <= 0)
486 	rollback_net_savepoint (sqlite, cache);
487     else
488 	release_net_savepoint (sqlite, cache);
489     if (point != NULL)
490       {
491 	  gaiaFreeGeomColl (point);
492 	  point = NULL;
493       }
494     if (ret <= 0)
495       {
496 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
497 	  gaianet_set_last_error_msg (accessor, msg);
498 	  sqlite3_result_error (context, msg, -1);
499 	  return;
500       }
501     sqlite3_result_int64 (context, ret);
502     return;
503 
504   no_net:
505     if (point != NULL)
506 	gaiaFreeGeomColl (point);
507     sqlite3_result_error (context,
508 			  "SQL/MM Spatial exception - invalid network name.",
509 			  -1);
510     return;
511 
512   null_arg:
513     if (point != NULL)
514 	gaiaFreeGeomColl (point);
515     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
516 			  -1);
517     return;
518 
519   invalid_arg:
520     if (point != NULL)
521 	gaiaFreeGeomColl (point);
522     sqlite3_result_error (context,
523 			  "SQL/MM Spatial exception - invalid argument.", -1);
524     return;
525 
526   invalid_geom:
527     if (point != NULL)
528 	gaiaFreeGeomColl (point);
529     sqlite3_result_error (context,
530 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
531 			  -1);
532     return;
533 
534   spatial_err:
535     if (point != NULL)
536 	gaiaFreeGeomColl (point);
537     sqlite3_result_error (context,
538 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
539 			  -1);
540     return;
541 
542   logical_err:
543     if (point != NULL)
544 	gaiaFreeGeomColl (point);
545     sqlite3_result_error (context,
546 			  "SQL/MM Spatial exception - Logical Network can't accept not null geometry.",
547 			  -1);
548     return;
549 }
550 
551 SPATIALITE_PRIVATE void
fnctaux_MoveIsoNetNode(const void * xcontext,int argc,const void * xargv)552 fnctaux_MoveIsoNetNode (const void *xcontext, int argc, const void *xargv)
553 {
554 /* SQL function:
555 / ST_MoveIsoNetNode ( text network-name, int node_id, Geometry point )
556 /
557 / returns: TEXT (description of new location)
558 / raises an exception on failure
559 */
560     char xid[80];
561     char *newpos = NULL;
562     int ret;
563     const char *net_name;
564     sqlite3_int64 node_id;
565     unsigned char *p_blob;
566     int n_bytes;
567     gaiaGeomCollPtr point = NULL;
568     gaiaPointPtr pt = NULL;
569     int invalid = 0;
570     GaiaNetworkAccessorPtr accessor;
571     struct gaia_network *net;
572     int gpkg_amphibious = 0;
573     int gpkg_mode = 0;
574     sqlite3_context *context = (sqlite3_context *) xcontext;
575     sqlite3_value **argv = (sqlite3_value **) xargv;
576     sqlite3 *sqlite = sqlite3_context_db_handle (context);
577     struct splite_internal_cache *cache = sqlite3_user_data (context);
578     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
579     if (cache != NULL)
580       {
581 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
582 	  gpkg_mode = cache->gpkg_mode;
583       }
584     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
585 	goto null_arg;
586     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
587 	net_name = (const char *) sqlite3_value_text (argv[0]);
588     else
589 	goto invalid_arg;
590     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
591 	goto null_arg;
592     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
593 	node_id = sqlite3_value_int64 (argv[1]);
594     else
595 	goto invalid_arg;
596 
597 /* attempting to get a Network Accessor */
598     accessor = gaiaGetNetwork (sqlite, cache, net_name);
599     if (accessor == NULL)
600 	goto no_net;
601     net = (struct gaia_network *) accessor;
602 
603     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
604       {
605 	  if (net->spatial)
606 	      goto spatial_err;
607       }
608     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
609       {
610 	  if (net->spatial == 0)
611 	      goto logical_err;
612 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
613 	  n_bytes = sqlite3_value_bytes (argv[2]);
614 
615 	  /* attempting to get a Point Geometry */
616 	  point =
617 	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
618 					   gpkg_amphibious);
619 	  if (!point)
620 	      goto invalid_arg;
621 	  if (point->FirstLinestring != NULL)
622 	      invalid = 1;
623 	  if (point->FirstPolygon != NULL)
624 	      invalid = 1;
625 	  if (point->FirstPoint != point->LastPoint
626 	      || point->FirstPoint == NULL)
627 	      invalid = 1;
628 	  if (invalid)
629 	      goto invalid_arg;
630 	  if (!check_matching_srid_dims
631 	      (accessor, point->Srid, point->DimensionModel))
632 	      goto invalid_geom;
633 	  pt = point->FirstPoint;
634       }
635     else
636 	goto invalid_arg;
637     sprintf (xid, "%lld", node_id);
638     if (pt == NULL)
639 	newpos =
640 	    sqlite3_mprintf ("Isolated Node %s moved to NULL location", xid);
641     else
642 	newpos =
643 	    sqlite3_mprintf ("Isolated Node %s moved to location %f,%f", xid,
644 			     pt->X, pt->Y);
645 
646     gaianet_reset_last_error_msg (accessor);
647     start_net_savepoint (sqlite, cache);
648     ret = gaiaMoveIsoNetNode (accessor, node_id, pt);
649     if (!ret)
650 	rollback_net_savepoint (sqlite, cache);
651     else
652 	release_net_savepoint (sqlite, cache);
653     if (point != NULL)
654 	gaiaFreeGeomColl (point);
655     point = NULL;
656     if (!ret)
657       {
658 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
659 	  gaianet_set_last_error_msg (accessor, msg);
660 	  sqlite3_result_error (context, msg, -1);
661 	  if (newpos != NULL)
662 	      sqlite3_free (newpos);
663 	  return;
664       }
665     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
666     return;
667 
668   no_net:
669     if (newpos != NULL)
670 	sqlite3_free (newpos);
671     if (point != NULL)
672 	gaiaFreeGeomColl (point);
673     sqlite3_result_error (context,
674 			  "SQL/MM Spatial exception - invalid network name.",
675 			  -1);
676     return;
677 
678   null_arg:
679     if (newpos != NULL)
680 	sqlite3_free (newpos);
681     if (point != NULL)
682 	gaiaFreeGeomColl (point);
683     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
684 			  -1);
685     return;
686 
687   invalid_arg:
688     if (newpos != NULL)
689 	sqlite3_free (newpos);
690     if (point != NULL)
691 	gaiaFreeGeomColl (point);
692     sqlite3_result_error (context,
693 			  "SQL/MM Spatial exception - invalid argument.", -1);
694     return;
695 
696   invalid_geom:
697     if (newpos != NULL)
698 	sqlite3_free (newpos);
699     if (point != NULL)
700 	gaiaFreeGeomColl (point);
701     sqlite3_result_error (context,
702 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
703 			  -1);
704     return;
705 
706   spatial_err:
707     if (point != NULL)
708 	gaiaFreeGeomColl (point);
709     sqlite3_result_error (context,
710 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
711 			  -1);
712     return;
713 
714   logical_err:
715     if (point != NULL)
716 	gaiaFreeGeomColl (point);
717     sqlite3_result_error (context,
718 			  "SQL/MM Spatial exception - Logical Network can't accept not null geometry.",
719 			  -1);
720     return;
721 }
722 
723 SPATIALITE_PRIVATE void
fnctaux_RemIsoNetNode(const void * xcontext,int argc,const void * xargv)724 fnctaux_RemIsoNetNode (const void *xcontext, int argc, const void *xargv)
725 {
726 /* SQL function:
727 / ST_RemIsoNetNode ( text network-name, int node_id )
728 /
729 / returns: TEXT (description of operation)
730 / raises an exception on failure
731 */
732     char xid[80];
733     char *newpos = NULL;
734     int ret;
735     const char *network_name;
736     sqlite3_int64 node_id;
737     GaiaNetworkAccessorPtr accessor;
738     struct gaia_network *net;
739     sqlite3_context *context = (sqlite3_context *) xcontext;
740     sqlite3_value **argv = (sqlite3_value **) xargv;
741     sqlite3 *sqlite = sqlite3_context_db_handle (context);
742     struct splite_internal_cache *cache = sqlite3_user_data (context);
743     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
744     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
745 	goto null_arg;
746     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
747 	network_name = (const char *) sqlite3_value_text (argv[0]);
748     else
749 	goto invalid_arg;
750     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
751 	goto null_arg;
752     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
753 	node_id = sqlite3_value_int64 (argv[1]);
754     else
755 	goto invalid_arg;
756 
757 /* attempting to get a Network Accessor */
758     accessor = gaiaGetNetwork (sqlite, cache, network_name);
759     if (accessor == NULL)
760 	goto no_net;
761     net = (struct gaia_network *) accessor;
762     sprintf (xid, "%lld", node_id);
763     newpos = sqlite3_mprintf ("Isolated NetNode %s removed", xid);
764 
765     gaianet_reset_last_error_msg (accessor);
766     start_net_savepoint (sqlite, cache);
767     ret = gaiaRemIsoNetNode (accessor, node_id);
768     if (!ret)
769 	rollback_net_savepoint (sqlite, cache);
770     else
771 	release_net_savepoint (sqlite, cache);
772     if (!ret)
773       {
774 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
775 	  gaianet_set_last_error_msg (accessor, msg);
776 	  sqlite3_result_error (context, msg, -1);
777 	  if (newpos != NULL)
778 	      sqlite3_free (newpos);
779 	  return;
780       }
781     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
782     return;
783 
784   no_net:
785     if (newpos != NULL)
786 	sqlite3_free (newpos);
787     sqlite3_result_error (context,
788 			  "SQL/MM Spatial exception - invalid network name.",
789 			  -1);
790     return;
791 
792   null_arg:
793     if (newpos != NULL)
794 	sqlite3_free (newpos);
795     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
796 			  -1);
797     return;
798 
799   invalid_arg:
800     if (newpos != NULL)
801 	sqlite3_free (newpos);
802     sqlite3_result_error (context,
803 			  "SQL/MM Spatial exception - invalid argument.", -1);
804     return;
805 }
806 
807 SPATIALITE_PRIVATE void
fnctaux_AddLink(const void * xcontext,int argc,const void * xargv)808 fnctaux_AddLink (const void *xcontext, int argc, const void *xargv)
809 {
810 /* SQL function:
811 / ST_AddLink ( text network-name, int start_node_id, int end_node_id, Geometry linestring )
812 /
813 / returns: the ID of the inserted Link on success, 0 on failure
814 / raises an exception on failure
815 */
816     sqlite3_int64 ret;
817     const char *network_name;
818     sqlite3_int64 start_node_id;
819     sqlite3_int64 end_node_id;
820     unsigned char *p_blob;
821     int n_bytes;
822     gaiaGeomCollPtr line = NULL;
823     gaiaLinestringPtr ln = NULL;
824     int invalid = 0;
825     GaiaNetworkAccessorPtr accessor;
826     struct gaia_network *net;
827     int gpkg_amphibious = 0;
828     int gpkg_mode = 0;
829     sqlite3_context *context = (sqlite3_context *) xcontext;
830     sqlite3_value **argv = (sqlite3_value **) xargv;
831     sqlite3 *sqlite = sqlite3_context_db_handle (context);
832     struct splite_internal_cache *cache = sqlite3_user_data (context);
833     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
834     if (cache != NULL)
835       {
836 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
837 	  gpkg_mode = cache->gpkg_mode;
838       }
839     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
840 	goto null_arg;
841     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
842 	network_name = (const char *) sqlite3_value_text (argv[0]);
843     else
844 	goto invalid_arg;
845     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
846 	goto null_arg;
847     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
848 	start_node_id = sqlite3_value_int64 (argv[1]);
849     else
850 	goto invalid_arg;
851     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
852 	goto null_arg;
853     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
854 	end_node_id = sqlite3_value_int64 (argv[2]);
855     else
856 	goto invalid_arg;
857 
858 /* attempting to get a Network Accessor */
859     accessor = gaiaGetNetwork (sqlite, cache, network_name);
860     if (accessor == NULL)
861 	goto no_net;
862     net = (struct gaia_network *) accessor;
863 
864     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
865       {
866 	  if (net->spatial)
867 	      goto spatial_err;
868       }
869     else if (sqlite3_value_type (argv[3]) == SQLITE_BLOB)
870       {
871 	  if (net->spatial == 0)
872 	      goto logical_err;
873 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[3]);
874 	  n_bytes = sqlite3_value_bytes (argv[3]);
875 
876 /* attempting to get a Linestring Geometry */
877 	  line =
878 	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
879 					   gpkg_amphibious);
880 	  if (!line)
881 	      goto invalid_arg;
882 	  if (line->FirstPoint != NULL)
883 	      invalid = 1;
884 	  if (line->FirstPolygon != NULL)
885 	      invalid = 1;
886 	  if (line->FirstLinestring != line->LastLinestring
887 	      || line->FirstLinestring == NULL)
888 	      invalid = 1;
889 	  if (invalid)
890 	      goto invalid_arg;
891 	  if (!check_matching_srid_dims
892 	      (accessor, line->Srid, line->DimensionModel))
893 	      goto invalid_geom;
894 	  ln = line->FirstLinestring;
895       }
896     else
897 	goto invalid_arg;
898 
899     gaianet_reset_last_error_msg (accessor);
900     start_net_savepoint (sqlite, cache);
901     ret = gaiaAddLink (accessor, start_node_id, end_node_id, ln);
902     if (ret <= 0)
903 	rollback_net_savepoint (sqlite, cache);
904     else
905 	release_net_savepoint (sqlite, cache);
906     if (line != NULL)
907 	gaiaFreeGeomColl (line);
908     line = NULL;
909     if (ret <= 0)
910       {
911 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
912 	  gaianet_set_last_error_msg (accessor, msg);
913 	  sqlite3_result_error (context, msg, -1);
914 	  return;
915       }
916     sqlite3_result_int64 (context, ret);
917     return;
918 
919   no_net:
920     if (line != NULL)
921 	gaiaFreeGeomColl (line);
922     sqlite3_result_error (context,
923 			  "SQL/MM Spatial exception - invalid network name.",
924 			  -1);
925     return;
926 
927   null_arg:
928     if (line != NULL)
929 	gaiaFreeGeomColl (line);
930     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
931 			  -1);
932     return;
933 
934   invalid_arg:
935     if (line != NULL)
936 	gaiaFreeGeomColl (line);
937     sqlite3_result_error (context,
938 			  "SQL/MM Spatial exception - invalid argument.", -1);
939     return;
940 
941   spatial_err:
942     if (line != NULL)
943 	gaiaFreeGeomColl (line);
944     sqlite3_result_error (context,
945 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
946 			  -1);
947     return;
948 
949   logical_err:
950     if (line != NULL)
951 	gaiaFreeGeomColl (line);
952     sqlite3_result_error (context,
953 			  "SQL/MM Spatial exception - Logical Network can't accept not null geometry.",
954 			  -1);
955     return;
956 
957   invalid_geom:
958     if (line != NULL)
959 	gaiaFreeGeomColl (line);
960     sqlite3_result_error (context,
961 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
962 			  -1);
963     return;
964 }
965 
966 SPATIALITE_PRIVATE void
fnctaux_ChangeLinkGeom(const void * xcontext,int argc,const void * xargv)967 fnctaux_ChangeLinkGeom (const void *xcontext, int argc, const void *xargv)
968 {
969 /* SQL function:
970 / ST_ChangeLinkGeom ( text network-name, int link_id, Geometry linestring )
971 /
972 / returns: TEXT (description of operation)
973 / raises an exception on failure
974 */
975     char xid[80];
976     char *newpos = NULL;
977     int ret;
978     const char *network_name;
979     sqlite3_int64 link_id;
980     unsigned char *p_blob;
981     int n_bytes;
982     gaiaGeomCollPtr line = NULL;
983     gaiaLinestringPtr ln = NULL;
984     int invalid = 0;
985     GaiaNetworkAccessorPtr accessor;
986     struct gaia_network *net;
987     int gpkg_amphibious = 0;
988     int gpkg_mode = 0;
989     sqlite3_context *context = (sqlite3_context *) xcontext;
990     sqlite3_value **argv = (sqlite3_value **) xargv;
991     sqlite3 *sqlite = sqlite3_context_db_handle (context);
992     struct splite_internal_cache *cache = sqlite3_user_data (context);
993     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
994     if (cache != NULL)
995       {
996 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
997 	  gpkg_mode = cache->gpkg_mode;
998       }
999     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1000 	goto null_arg;
1001     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1002 	network_name = (const char *) sqlite3_value_text (argv[0]);
1003     else
1004 	goto invalid_arg;
1005     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1006 	goto null_arg;
1007     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1008 	link_id = sqlite3_value_int64 (argv[1]);
1009     else
1010 	goto invalid_arg;
1011 
1012 /* attempting to get a Network Accessor */
1013     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1014     if (accessor == NULL)
1015 	goto no_net;
1016     net = (struct gaia_network *) accessor;
1017 
1018     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1019       {
1020 	  if (net->spatial)
1021 	      goto spatial_err;
1022       }
1023     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1024       {
1025 	  if (net->spatial == 0)
1026 	      goto logical_err;
1027 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1028 	  n_bytes = sqlite3_value_bytes (argv[2]);
1029 
1030 	  /* attempting to get a Linestring Geometry */
1031 	  line =
1032 	      gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1033 					   gpkg_amphibious);
1034 	  if (!line)
1035 	      goto invalid_arg;
1036 	  if (line->FirstPoint != NULL)
1037 	      invalid = 1;
1038 	  if (line->FirstPolygon != NULL)
1039 	      invalid = 1;
1040 	  if (line->FirstLinestring != line->LastLinestring
1041 	      || line->FirstLinestring == NULL)
1042 	      invalid = 1;
1043 	  if (invalid)
1044 	      goto invalid_arg;
1045 
1046 	  if (!check_matching_srid_dims
1047 	      (accessor, line->Srid, line->DimensionModel))
1048 	      goto invalid_geom;
1049 	  ln = line->FirstLinestring;
1050       }
1051     else
1052 	goto invalid_arg;
1053     sprintf (xid, "%lld", link_id);
1054     newpos = sqlite3_mprintf ("Link %s changed", xid);
1055 
1056     gaianet_reset_last_error_msg (accessor);
1057     start_net_savepoint (sqlite, cache);
1058     ret = gaiaChangeLinkGeom (accessor, link_id, ln);
1059     if (!ret)
1060 	rollback_net_savepoint (sqlite, cache);
1061     else
1062 	release_net_savepoint (sqlite, cache);
1063     if (line != NULL)
1064 	gaiaFreeGeomColl (line);
1065     line = NULL;
1066     if (!ret)
1067       {
1068 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1069 	  gaianet_set_last_error_msg (accessor, msg);
1070 	  sqlite3_result_error (context, msg, -1);
1071 	  if (newpos != NULL)
1072 	      sqlite3_free (newpos);
1073 	  return;
1074       }
1075     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
1076     return;
1077 
1078   no_net:
1079     if (newpos != NULL)
1080 	sqlite3_free (newpos);
1081     if (line != NULL)
1082 	gaiaFreeGeomColl (line);
1083     sqlite3_result_error (context,
1084 			  "SQL/MM Spatial exception - invalid network name.",
1085 			  -1);
1086     return;
1087 
1088   null_arg:
1089     if (newpos != NULL)
1090 	sqlite3_free (newpos);
1091     if (line != NULL)
1092 	gaiaFreeGeomColl (line);
1093     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1094 			  -1);
1095     return;
1096 
1097   invalid_arg:
1098     if (newpos != NULL)
1099 	sqlite3_free (newpos);
1100     if (line != NULL)
1101 	gaiaFreeGeomColl (line);
1102     sqlite3_result_error (context,
1103 			  "SQL/MM Spatial exception - invalid argument.", -1);
1104     return;
1105 
1106   invalid_geom:
1107     if (newpos != NULL)
1108 	sqlite3_free (newpos);
1109     if (line != NULL)
1110 	gaiaFreeGeomColl (line);
1111     sqlite3_result_error (context,
1112 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
1113 			  -1);
1114     return;
1115 
1116   spatial_err:
1117     if (line != NULL)
1118 	gaiaFreeGeomColl (line);
1119     sqlite3_result_error (context,
1120 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
1121 			  -1);
1122     return;
1123 
1124   logical_err:
1125     if (line != NULL)
1126 	gaiaFreeGeomColl (line);
1127     sqlite3_result_error (context,
1128 			  "SQL/MM Spatial exception - Logical Network can't accept not null geometry.",
1129 			  -1);
1130     return;
1131 }
1132 
1133 SPATIALITE_PRIVATE void
fnctaux_RemoveLink(const void * xcontext,int argc,const void * xargv)1134 fnctaux_RemoveLink (const void *xcontext, int argc, const void *xargv)
1135 {
1136 /* SQL function:
1137 / ST_RemoveLink ( text network-name, int link_id )
1138 /
1139 / returns: TEXT (description of operation)
1140 / raises an exception on failure
1141 */
1142     char xid[80];
1143     char *newpos = NULL;
1144     int ret;
1145     const char *network_name;
1146     sqlite3_int64 link_id;
1147     GaiaNetworkAccessorPtr accessor;
1148     struct gaia_network *net;
1149     sqlite3_context *context = (sqlite3_context *) xcontext;
1150     sqlite3_value **argv = (sqlite3_value **) xargv;
1151     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1152     struct splite_internal_cache *cache = sqlite3_user_data (context);
1153     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1154     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1155 	goto null_arg;
1156     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1157 	network_name = (const char *) sqlite3_value_text (argv[0]);
1158     else
1159 	goto invalid_arg;
1160     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1161 	goto null_arg;
1162     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1163 	link_id = sqlite3_value_int64 (argv[1]);
1164     else
1165 	goto invalid_arg;
1166 
1167 /* attempting to get a Network Accessor */
1168     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1169     if (accessor == NULL)
1170 	goto no_net;
1171     net = (struct gaia_network *) accessor;
1172     sprintf (xid, "%lld", link_id);
1173     newpos = sqlite3_mprintf ("Link %s removed", xid);
1174 
1175     gaianet_reset_last_error_msg (accessor);
1176     start_net_savepoint (sqlite, cache);
1177     ret = gaiaRemoveLink (accessor, link_id);
1178     if (!ret)
1179 	rollback_net_savepoint (sqlite, cache);
1180     else
1181 	release_net_savepoint (sqlite, cache);
1182     if (!ret)
1183       {
1184 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1185 	  gaianet_set_last_error_msg (accessor, msg);
1186 	  sqlite3_result_error (context, msg, -1);
1187 	  if (newpos != NULL)
1188 	      sqlite3_free (newpos);
1189 	  return;
1190       }
1191     sqlite3_result_text (context, newpos, strlen (newpos), sqlite3_free);
1192     return;
1193 
1194   no_net:
1195     if (newpos != NULL)
1196 	sqlite3_free (newpos);
1197     sqlite3_result_error (context,
1198 			  "SQL/MM Spatial exception - invalid network name.",
1199 			  -1);
1200     return;
1201 
1202   null_arg:
1203     if (newpos != NULL)
1204 	sqlite3_free (newpos);
1205     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1206 			  -1);
1207     return;
1208 
1209   invalid_arg:
1210     if (newpos != NULL)
1211 	sqlite3_free (newpos);
1212     sqlite3_result_error (context,
1213 			  "SQL/MM Spatial exception - invalid argument.", -1);
1214     return;
1215 }
1216 
1217 SPATIALITE_PRIVATE void
fnctaux_NewLogLinkSplit(const void * xcontext,int argc,const void * xargv)1218 fnctaux_NewLogLinkSplit (const void *xcontext, int argc, const void *xargv)
1219 {
1220 /* SQL function:
1221 / ST_NewLogLinkSplit ( text network-name, int link_id )
1222 /
1223 / returns: the ID of the inserted Node on success
1224 / raises an exception on failure
1225 */
1226     sqlite3_int64 ret;
1227     const char *network_name;
1228     sqlite3_int64 link_id;
1229     GaiaNetworkAccessorPtr accessor;
1230     struct gaia_network *net;
1231     sqlite3_context *context = (sqlite3_context *) xcontext;
1232     sqlite3_value **argv = (sqlite3_value **) xargv;
1233     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1234     struct splite_internal_cache *cache = sqlite3_user_data (context);
1235     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1236     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1237 	goto null_arg;
1238     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1239 	network_name = (const char *) sqlite3_value_text (argv[0]);
1240     else
1241 	goto invalid_arg;
1242     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1243 	goto null_arg;
1244     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1245 	link_id = sqlite3_value_int64 (argv[1]);
1246     else
1247 	goto invalid_arg;
1248 
1249 /* attempting to get a Network Accessor */
1250     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1251     if (accessor == NULL)
1252 	goto no_net;
1253     net = (struct gaia_network *) accessor;
1254     if (net->spatial)
1255 	goto spatial_err;
1256 
1257     gaianet_reset_last_error_msg (accessor);
1258     start_net_savepoint (sqlite, cache);
1259     ret = gaiaNewLogLinkSplit (accessor, link_id);
1260     if (ret <= 0)
1261 	rollback_net_savepoint (sqlite, cache);
1262     else
1263 	release_net_savepoint (sqlite, cache);
1264     if (ret <= 0)
1265       {
1266 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1267 	  gaianet_set_last_error_msg (accessor, msg);
1268 	  sqlite3_result_error (context, msg, -1);
1269 	  return;
1270       }
1271     sqlite3_result_int64 (context, ret);
1272     return;
1273 
1274   no_net:
1275     sqlite3_result_error (context,
1276 			  "SQL/MM Spatial exception - invalid network name.",
1277 			  -1);
1278     return;
1279 
1280   null_arg:
1281     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1282 			  -1);
1283     return;
1284 
1285   invalid_arg:
1286     sqlite3_result_error (context,
1287 			  "SQL/MM Spatial exception - invalid argument.", -1);
1288     return;
1289 
1290   spatial_err:
1291     sqlite3_result_error (context,
1292 			  "SQL/MM Spatial exception - ST_NewLogLinkSplit can't support Spatial Network; try using ST_NewGeoLinkSplit.",
1293 			  -1);
1294     return;
1295 }
1296 
1297 SPATIALITE_PRIVATE void
fnctaux_ModLogLinkSplit(const void * xcontext,int argc,const void * xargv)1298 fnctaux_ModLogLinkSplit (const void *xcontext, int argc, const void *xargv)
1299 {
1300 /* SQL function:
1301 / ST_ModLogLinkSplit ( text network-name, int link_id )
1302 /
1303 / returns: the ID of the inserted Node on success
1304 / raises an exception on failure
1305 */
1306     sqlite3_int64 ret;
1307     const char *network_name;
1308     sqlite3_int64 link_id;
1309     GaiaNetworkAccessorPtr accessor;
1310     struct gaia_network *net;
1311     sqlite3_context *context = (sqlite3_context *) xcontext;
1312     sqlite3_value **argv = (sqlite3_value **) xargv;
1313     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1314     struct splite_internal_cache *cache = sqlite3_user_data (context);
1315     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1316     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1317 	goto null_arg;
1318     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1319 	network_name = (const char *) sqlite3_value_text (argv[0]);
1320     else
1321 	goto invalid_arg;
1322     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1323 	goto null_arg;
1324     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1325 	link_id = sqlite3_value_int64 (argv[1]);
1326     else
1327 	goto invalid_arg;
1328 
1329 /* attempting to get a Network Accessor */
1330     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1331     if (accessor == NULL)
1332 	goto no_net;
1333     net = (struct gaia_network *) accessor;
1334     if (net->spatial)
1335 	goto spatial_err;
1336 
1337     gaianet_reset_last_error_msg (accessor);
1338     start_net_savepoint (sqlite, cache);
1339     ret = gaiaModLogLinkSplit (accessor, link_id);
1340     if (ret <= 0)
1341 	rollback_net_savepoint (sqlite, cache);
1342     else
1343 	release_net_savepoint (sqlite, cache);
1344     if (ret <= 0)
1345       {
1346 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1347 	  gaianet_set_last_error_msg (accessor, msg);
1348 	  sqlite3_result_error (context, msg, -1);
1349 	  return;
1350       }
1351     sqlite3_result_int64 (context, ret);
1352     return;
1353 
1354   no_net:
1355     sqlite3_result_error (context,
1356 			  "SQL/MM Spatial exception - invalid network name.",
1357 			  -1);
1358     return;
1359 
1360   null_arg:
1361     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1362 			  -1);
1363     return;
1364 
1365   invalid_arg:
1366     sqlite3_result_error (context,
1367 			  "SQL/MM Spatial exception - invalid argument.", -1);
1368     return;
1369 
1370   spatial_err:
1371     sqlite3_result_error (context,
1372 			  "SQL/MM Spatial exception - ST_ModLogLinkSplit can't support Spatial Network; try using ST_ModGeoLinkSplit.",
1373 			  -1);
1374     return;
1375 }
1376 
1377 SPATIALITE_PRIVATE void
fnctaux_NewGeoLinkSplit(const void * xcontext,int argc,const void * xargv)1378 fnctaux_NewGeoLinkSplit (const void *xcontext, int argc, const void *xargv)
1379 {
1380 /* SQL function:
1381 / ST_NewGeoLinkSplit ( text network-name, int link_id, Geometry point )
1382 /
1383 / returns: the ID of the inserted Node on success
1384 / raises an exception on failure
1385 */
1386     sqlite3_int64 ret;
1387     const char *network_name;
1388     sqlite3_int64 link_id;
1389     unsigned char *p_blob;
1390     int n_bytes;
1391     gaiaGeomCollPtr point = NULL;
1392     gaiaPointPtr pt;
1393     int invalid = 0;
1394     GaiaNetworkAccessorPtr accessor;
1395     struct gaia_network *net;
1396     int gpkg_amphibious = 0;
1397     int gpkg_mode = 0;
1398     sqlite3_context *context = (sqlite3_context *) xcontext;
1399     sqlite3_value **argv = (sqlite3_value **) xargv;
1400     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1401     struct splite_internal_cache *cache = sqlite3_user_data (context);
1402     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1403     if (cache != NULL)
1404       {
1405 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1406 	  gpkg_mode = cache->gpkg_mode;
1407       }
1408     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1409 	goto null_arg;
1410     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1411 	network_name = (const char *) sqlite3_value_text (argv[0]);
1412     else
1413 	goto invalid_arg;
1414     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1415 	goto null_arg;
1416     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1417 	link_id = sqlite3_value_int64 (argv[1]);
1418     else
1419 	goto invalid_arg;
1420 
1421 /* attempting to get a Network Accessor */
1422     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1423     if (accessor == NULL)
1424 	goto no_net;
1425     net = (struct gaia_network *) accessor;
1426     if (net->spatial == 0)
1427 	goto logical_err;
1428 
1429     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1430 	goto spatial_err;
1431     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1432       {
1433 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1434 	  n_bytes = sqlite3_value_bytes (argv[2]);
1435       }
1436     else
1437 	goto invalid_arg;
1438 
1439 /* attempting to get a Point Geometry */
1440     point =
1441 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1442 				     gpkg_amphibious);
1443     if (!point)
1444 	goto invalid_arg;
1445     if (point->FirstLinestring != NULL)
1446 	invalid = 1;
1447     if (point->FirstPolygon != NULL)
1448 	invalid = 1;
1449     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
1450 	invalid = 1;
1451     if (invalid)
1452 	goto invalid_arg;
1453     if (!check_matching_srid_dims
1454 	(accessor, point->Srid, point->DimensionModel))
1455 	goto invalid_geom;
1456     pt = point->FirstPoint;
1457 
1458     gaianet_reset_last_error_msg (accessor);
1459     start_net_savepoint (sqlite, cache);
1460     ret = gaiaNewGeoLinkSplit (accessor, link_id, pt);
1461     if (ret <= 0)
1462 	rollback_net_savepoint (sqlite, cache);
1463     else
1464 	release_net_savepoint (sqlite, cache);
1465     gaiaFreeGeomColl (point);
1466     point = NULL;
1467     if (ret <= 0)
1468       {
1469 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1470 	  gaianet_set_last_error_msg (accessor, msg);
1471 	  sqlite3_result_error (context, msg, -1);
1472 	  return;
1473       }
1474     sqlite3_result_int64 (context, ret);
1475     return;
1476 
1477   no_net:
1478     if (point != NULL)
1479 	gaiaFreeGeomColl (point);
1480     sqlite3_result_error (context,
1481 			  "SQL/MM Spatial exception - invalid network name.",
1482 			  -1);
1483     return;
1484 
1485   null_arg:
1486     if (point != NULL)
1487 	gaiaFreeGeomColl (point);
1488     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1489 			  -1);
1490     return;
1491 
1492   invalid_arg:
1493     if (point != NULL)
1494 	gaiaFreeGeomColl (point);
1495     sqlite3_result_error (context,
1496 			  "SQL/MM Spatial exception - invalid argument.", -1);
1497     return;
1498 
1499   invalid_geom:
1500     if (point != NULL)
1501 	gaiaFreeGeomColl (point);
1502     sqlite3_result_error (context,
1503 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
1504 			  -1);
1505     return;
1506 
1507   spatial_err:
1508     if (point != NULL)
1509 	gaiaFreeGeomColl (point);
1510     sqlite3_result_error (context,
1511 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
1512 			  -1);
1513     return;
1514 
1515   logical_err:
1516     if (point != NULL)
1517 	gaiaFreeGeomColl (point);
1518     sqlite3_result_error (context,
1519 			  "SQL/MM Spatial exception - ST_NewGeoLinkSplit can't support Logical Network; try using ST_NewLogLinkSplit.",
1520 			  -1);
1521     return;
1522 }
1523 
1524 SPATIALITE_PRIVATE void
fnctaux_ModGeoLinkSplit(const void * xcontext,int argc,const void * xargv)1525 fnctaux_ModGeoLinkSplit (const void *xcontext, int argc, const void *xargv)
1526 {
1527 /* SQL function:
1528 / ST_ModGeoLinkSplit ( text network-name, int link_id, Geometry point )
1529 /
1530 / returns: the ID of the inserted Node on success
1531 / raises an exception on failure
1532 */
1533     sqlite3_int64 ret;
1534     const char *network_name;
1535     sqlite3_int64 link_id;
1536     unsigned char *p_blob;
1537     int n_bytes;
1538     gaiaGeomCollPtr point = NULL;
1539     gaiaPointPtr pt;
1540     int invalid = 0;
1541     GaiaNetworkAccessorPtr accessor;
1542     struct gaia_network *net;
1543     int gpkg_amphibious = 0;
1544     int gpkg_mode = 0;
1545     sqlite3_context *context = (sqlite3_context *) xcontext;
1546     sqlite3_value **argv = (sqlite3_value **) xargv;
1547     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1548     struct splite_internal_cache *cache = sqlite3_user_data (context);
1549     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1550     if (cache != NULL)
1551       {
1552 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
1553 	  gpkg_mode = cache->gpkg_mode;
1554       }
1555     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1556 	goto null_arg;
1557     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1558 	network_name = (const char *) sqlite3_value_text (argv[0]);
1559     else
1560 	goto invalid_arg;
1561     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1562 	goto null_arg;
1563     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1564 	link_id = sqlite3_value_int64 (argv[1]);
1565     else
1566 	goto invalid_arg;
1567 
1568 /* attempting to get a Network Accessor */
1569     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1570     if (accessor == NULL)
1571 	goto no_net;
1572     net = (struct gaia_network *) accessor;
1573     if (net->spatial == 0)
1574 	goto logical_err;
1575 
1576     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1577 	goto spatial_err;
1578     else if (sqlite3_value_type (argv[2]) == SQLITE_BLOB)
1579       {
1580 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[2]);
1581 	  n_bytes = sqlite3_value_bytes (argv[2]);
1582       }
1583     else
1584 	goto invalid_arg;
1585 
1586 /* attempting to get a Point Geometry */
1587     point =
1588 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
1589 				     gpkg_amphibious);
1590     if (!point)
1591 	goto invalid_arg;
1592     if (point->FirstLinestring != NULL)
1593 	invalid = 1;
1594     if (point->FirstPolygon != NULL)
1595 	invalid = 1;
1596     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
1597 	invalid = 1;
1598     if (invalid)
1599 	goto invalid_arg;
1600     if (!check_matching_srid_dims
1601 	(accessor, point->Srid, point->DimensionModel))
1602 	goto invalid_geom;
1603     pt = point->FirstPoint;
1604 
1605     gaianet_reset_last_error_msg (accessor);
1606     start_net_savepoint (sqlite, cache);
1607     ret = gaiaModGeoLinkSplit (accessor, link_id, pt);
1608     if (ret <= 0)
1609 	rollback_net_savepoint (sqlite, cache);
1610     else
1611 	release_net_savepoint (sqlite, cache);
1612     gaiaFreeGeomColl (point);
1613     point = NULL;
1614     if (ret <= 0)
1615       {
1616 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1617 	  gaianet_set_last_error_msg (accessor, msg);
1618 	  sqlite3_result_error (context, msg, -1);
1619 	  return;
1620       }
1621     sqlite3_result_int64 (context, ret);
1622     return;
1623 
1624   no_net:
1625     if (point != NULL)
1626 	gaiaFreeGeomColl (point);
1627     sqlite3_result_error (context,
1628 			  "SQL/MM Spatial exception - invalid network name.",
1629 			  -1);
1630     return;
1631 
1632   null_arg:
1633     if (point != NULL)
1634 	gaiaFreeGeomColl (point);
1635     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1636 			  -1);
1637     return;
1638 
1639   invalid_arg:
1640     if (point != NULL)
1641 	gaiaFreeGeomColl (point);
1642     sqlite3_result_error (context,
1643 			  "SQL/MM Spatial exception - invalid argument.", -1);
1644     return;
1645 
1646   invalid_geom:
1647     if (point != NULL)
1648 	gaiaFreeGeomColl (point);
1649     sqlite3_result_error (context,
1650 			  "SQL/MM Spatial exception - invalid geometry (mismatching SRID or dimensions).",
1651 			  -1);
1652     return;
1653 
1654   spatial_err:
1655     if (point != NULL)
1656 	gaiaFreeGeomColl (point);
1657     sqlite3_result_error (context,
1658 			  "SQL/MM Spatial exception - Spatial Network can't accept null geometry.",
1659 			  -1);
1660     return;
1661 
1662   logical_err:
1663     if (point != NULL)
1664 	gaiaFreeGeomColl (point);
1665     sqlite3_result_error (context,
1666 			  "SQL/MM Spatial exception - ST_ModGeoLinkSplit can't support Logical Network; try using ST_ModLogLinkSplit.",
1667 			  -1);
1668     return;
1669 }
1670 
1671 SPATIALITE_PRIVATE void
fnctaux_NewLinkHeal(const void * xcontext,int argc,const void * xargv)1672 fnctaux_NewLinkHeal (const void *xcontext, int argc, const void *xargv)
1673 {
1674 /* SQL function:
1675 / ST_NewLinkHeal ( text network-name, int link_id, int anotherlink_id )
1676 /
1677 / returns: the ID of the removed Node on success
1678 / raises an exception on failure
1679 */
1680     sqlite3_int64 ret;
1681     const char *network_name;
1682     sqlite3_int64 link_id;
1683     sqlite3_int64 anotherlink_id;
1684     GaiaNetworkAccessorPtr accessor;
1685     struct gaia_network *net;
1686     sqlite3_context *context = (sqlite3_context *) xcontext;
1687     sqlite3_value **argv = (sqlite3_value **) xargv;
1688     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1689     struct splite_internal_cache *cache = sqlite3_user_data (context);
1690     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1691     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1692 	goto null_arg;
1693     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1694 	network_name = (const char *) sqlite3_value_text (argv[0]);
1695     else
1696 	goto invalid_arg;
1697     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1698 	goto null_arg;
1699     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1700 	link_id = sqlite3_value_int64 (argv[1]);
1701     else
1702 	goto invalid_arg;
1703     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1704 	goto null_arg;
1705     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
1706 	anotherlink_id = sqlite3_value_int64 (argv[2]);
1707     else
1708 	goto invalid_arg;
1709 
1710 /* attempting to get a Network Accessor */
1711     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1712     if (accessor == NULL)
1713 	goto no_net;
1714     net = (struct gaia_network *) accessor;
1715 
1716     gaianet_reset_last_error_msg (accessor);
1717     start_net_savepoint (sqlite, cache);
1718     ret = gaiaNewLinkHeal (accessor, link_id, anotherlink_id);
1719     if (ret <= 0)
1720 	rollback_net_savepoint (sqlite, cache);
1721     else
1722 	release_net_savepoint (sqlite, cache);
1723     if (ret <= 0)
1724       {
1725 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1726 	  gaianet_set_last_error_msg (accessor, msg);
1727 	  sqlite3_result_error (context, msg, -1);
1728 	  return;
1729       }
1730     sqlite3_result_int64 (context, ret);
1731     return;
1732 
1733   no_net:
1734     sqlite3_result_error (context,
1735 			  "SQL/MM Spatial exception - invalid network name.",
1736 			  -1);
1737     return;
1738 
1739   null_arg:
1740     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1741 			  -1);
1742     return;
1743 
1744   invalid_arg:
1745     sqlite3_result_error (context,
1746 			  "SQL/MM Spatial exception - invalid argument.", -1);
1747     return;
1748 }
1749 
1750 SPATIALITE_PRIVATE void
fnctaux_ModLinkHeal(const void * xcontext,int argc,const void * xargv)1751 fnctaux_ModLinkHeal (const void *xcontext, int argc, const void *xargv)
1752 {
1753 /* SQL function:
1754 / ST_ModLinkHeal ( text network-name, int link_id )
1755 /
1756 / returns: the ID of the removed Node on success
1757 / raises an exception on failure
1758 */
1759     sqlite3_int64 ret;
1760     const char *network_name;
1761     sqlite3_int64 link_id;
1762     sqlite3_int64 anotherlink_id;
1763     GaiaNetworkAccessorPtr accessor;
1764     struct gaia_network *net;
1765     sqlite3_context *context = (sqlite3_context *) xcontext;
1766     sqlite3_value **argv = (sqlite3_value **) xargv;
1767     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1768     struct splite_internal_cache *cache = sqlite3_user_data (context);
1769     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1770     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1771 	goto null_arg;
1772     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1773 	network_name = (const char *) sqlite3_value_text (argv[0]);
1774     else
1775 	goto invalid_arg;
1776     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1777 	goto null_arg;
1778     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
1779 	link_id = sqlite3_value_int64 (argv[1]);
1780     else
1781 	goto invalid_arg;
1782     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
1783 	goto null_arg;
1784     else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
1785 	anotherlink_id = sqlite3_value_int64 (argv[2]);
1786     else
1787 	goto invalid_arg;
1788 
1789 /* attempting to get a Network Accessor */
1790     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1791     if (accessor == NULL)
1792 	goto no_net;
1793     net = (struct gaia_network *) accessor;
1794 
1795     gaianet_reset_last_error_msg (accessor);
1796     start_net_savepoint (sqlite, cache);
1797     ret = gaiaModLinkHeal (accessor, link_id, anotherlink_id);
1798     if (ret <= 0)
1799 	rollback_net_savepoint (sqlite, cache);
1800     else
1801 	release_net_savepoint (sqlite, cache);
1802     if (ret <= 0)
1803       {
1804 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
1805 	  gaianet_set_last_error_msg (accessor, msg);
1806 	  sqlite3_result_error (context, msg, -1);
1807 	  return;
1808       }
1809     sqlite3_result_int64 (context, ret);
1810     return;
1811 
1812   no_net:
1813     sqlite3_result_error (context,
1814 			  "SQL/MM Spatial exception - invalid network name.",
1815 			  -1);
1816     return;
1817 
1818   null_arg:
1819     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
1820 			  -1);
1821     return;
1822 
1823   invalid_arg:
1824     sqlite3_result_error (context,
1825 			  "SQL/MM Spatial exception - invalid argument.", -1);
1826     return;
1827 }
1828 
1829 static int
check_empty_network(struct gaia_network * net)1830 check_empty_network (struct gaia_network *net)
1831 {
1832 /* checking for an empty Network */
1833     char *sql;
1834     char *table;
1835     char *xtable;
1836     int ret;
1837     int i;
1838     char **results;
1839     int rows;
1840     int columns;
1841     char *errMsg = NULL;
1842     int already_populated = 0;
1843 
1844 /* testing NODE */
1845     table = sqlite3_mprintf ("%s_node", net->network_name);
1846     xtable = gaiaDoubleQuotedSql (table);
1847     sqlite3_free (table);
1848     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\"", xtable);
1849     free (xtable);
1850     ret =
1851 	sqlite3_get_table (net->db_handle, sql, &results, &rows, &columns,
1852 			   &errMsg);
1853     sqlite3_free (sql);
1854     if (ret != SQLITE_OK)
1855       {
1856 	  sqlite3_free (errMsg);
1857 	  return 0;
1858       }
1859     for (i = 1; i <= rows; i++)
1860       {
1861 	  if (atoi (results[(i * columns) + 0]) > 0)
1862 	      already_populated = 1;
1863       }
1864     sqlite3_free_table (results);
1865     if (already_populated)
1866 	return 0;
1867 
1868 /* testing LINK */
1869     table = sqlite3_mprintf ("%s_link", net->network_name);
1870     xtable = gaiaDoubleQuotedSql (table);
1871     sqlite3_free (table);
1872     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\"", xtable);
1873     free (xtable);
1874     ret =
1875 	sqlite3_get_table (net->db_handle, sql, &results, &rows, &columns,
1876 			   &errMsg);
1877     sqlite3_free (sql);
1878     if (ret != SQLITE_OK)
1879       {
1880 	  sqlite3_free (errMsg);
1881 	  return 0;
1882       }
1883     for (i = 1; i <= rows; i++)
1884       {
1885 	  if (atoi (results[(i * columns) + 0]) > 0)
1886 	      already_populated = 1;
1887       }
1888     sqlite3_free_table (results);
1889     if (already_populated)
1890 	return 0;
1891 
1892     return 1;
1893 }
1894 
1895 static int
do_loginet_from_tgeo(struct gaia_network * net,struct gaia_topology * topo)1896 do_loginet_from_tgeo (struct gaia_network *net, struct gaia_topology *topo)
1897 {
1898 /* populating a Logical Network starting from an existing Topology */
1899     char *table;
1900     char *xtable1;
1901     char *xtable2;
1902     char *sql;
1903     char *errMsg;
1904     int ret;
1905 
1906 /* preparing the SQL statement - NODE */
1907     table = sqlite3_mprintf ("%s_node", net->network_name);
1908     xtable1 = gaiaDoubleQuotedSql (table);
1909     sqlite3_free (table);
1910     table = sqlite3_mprintf ("%s_node", topo->topology_name);
1911     xtable2 = gaiaDoubleQuotedSql (table);
1912     sqlite3_free (table);
1913     sql = sqlite3_mprintf ("INSERT INTO \"%s\" (node_id, geometry) "
1914 			   "SELECT node_id, NULL FROM MAIN.\"%s\"", xtable1,
1915 			   xtable2);
1916     free (xtable1);
1917     free (xtable2);
1918     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
1919     sqlite3_free (sql);
1920     if (ret != SQLITE_OK)
1921       {
1922 	  char *msg =
1923 	      sqlite3_mprintf ("ST_LogiNetFromTGeo() error: \"%s\"", errMsg);
1924 	  sqlite3_free (errMsg);
1925 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
1926 	  sqlite3_free (msg);
1927 	  return 0;
1928       }
1929 
1930 /* preparing the SQL statement - LINK */
1931     table = sqlite3_mprintf ("%s_link", net->network_name);
1932     xtable1 = gaiaDoubleQuotedSql (table);
1933     sqlite3_free (table);
1934     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
1935     xtable2 = gaiaDoubleQuotedSql (table);
1936     sqlite3_free (table);
1937     sql =
1938 	sqlite3_mprintf
1939 	("INSERT INTO MAIN.\"%s\" (link_id, start_node, end_node, geometry) "
1940 	 "SELECT edge_id, start_node, end_node, NULL FROM MAIN.\"%s\"", xtable1,
1941 	 xtable2);
1942     free (xtable1);
1943     free (xtable2);
1944     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
1945     sqlite3_free (sql);
1946     if (ret != SQLITE_OK)
1947       {
1948 	  char *msg =
1949 	      sqlite3_mprintf ("ST_LogiNetFromTGeo() error: \"%s\"", errMsg);
1950 	  sqlite3_free (errMsg);
1951 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
1952 	  sqlite3_free (msg);
1953 	  return 0;
1954       }
1955 
1956     return 1;
1957 }
1958 
1959 SPATIALITE_PRIVATE void
fnctaux_LogiNetFromTGeo(const void * xcontext,int argc,const void * xargv)1960 fnctaux_LogiNetFromTGeo (const void *xcontext, int argc, const void *xargv)
1961 {
1962 /* SQL function:
1963 / ST_LogiNetFromTGeo ( text network-name, text topology-name )
1964 /
1965 / returns: 1 on success
1966 / raises an exception on failure
1967 */
1968     int ret;
1969     const char *network_name;
1970     const char *topo_name;
1971     GaiaNetworkAccessorPtr accessor;
1972     struct gaia_network *net;
1973     GaiaTopologyAccessorPtr accessor2;
1974     struct gaia_topology *topo;
1975     sqlite3_context *context = (sqlite3_context *) xcontext;
1976     sqlite3_value **argv = (sqlite3_value **) xargv;
1977     sqlite3 *sqlite = sqlite3_context_db_handle (context);
1978     struct splite_internal_cache *cache = sqlite3_user_data (context);
1979     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
1980     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
1981 	goto null_arg;
1982     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
1983 	network_name = (const char *) sqlite3_value_text (argv[0]);
1984     else
1985 	goto invalid_arg;
1986     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
1987 	goto null_arg;
1988     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
1989 	topo_name = (const char *) sqlite3_value_text (argv[1]);
1990     else
1991 	goto invalid_arg;
1992 
1993 /* attempting to get a Network Accessor */
1994     accessor = gaiaGetNetwork (sqlite, cache, network_name);
1995     if (accessor == NULL)
1996 	goto no_net;
1997     net = (struct gaia_network *) accessor;
1998     if (net->spatial)
1999 	goto spatial_err;
2000     if (!check_empty_network (net))
2001 	goto non_empty;
2002 
2003 /* attempting to get a Topology Accessor */
2004     accessor2 = gaiaGetTopology (sqlite, cache, topo_name);
2005     if (accessor2 == NULL)
2006 	goto no_topo;
2007     topo = (struct gaia_topology *) accessor2;
2008 
2009     gaianet_reset_last_error_msg (accessor);
2010     start_net_savepoint (sqlite, cache);
2011     ret = do_loginet_from_tgeo (net, topo);
2012     if (ret <= 0)
2013 	rollback_net_savepoint (sqlite, cache);
2014     else
2015 	release_net_savepoint (sqlite, cache);
2016     if (ret <= 0)
2017       {
2018 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2019 	  gaianet_set_last_error_msg (accessor, msg);
2020 	  sqlite3_result_error (context, msg, -1);
2021 	  return;
2022       }
2023     sqlite3_result_int (context, 1);
2024     return;
2025 
2026   no_net:
2027     sqlite3_result_error (context,
2028 			  "SQL/MM Spatial exception - invalid network name.",
2029 			  -1);
2030     return;
2031 
2032   spatial_err:
2033     sqlite3_result_error (context,
2034 			  "ST_LogiNetFromTGeo() cannot be applied to Spatial Network.",
2035 			  -1);
2036     return;
2037 
2038   non_empty:
2039     sqlite3_result_error (context,
2040 			  "SQL/MM Spatial exception - non-empty network.", -1);
2041     return;
2042 
2043   no_topo:
2044     sqlite3_result_error (context,
2045 			  "SQL/MM Spatial exception - invalid topology name.",
2046 			  -1);
2047     return;
2048 
2049   null_arg:
2050     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2051 			  -1);
2052     return;
2053 
2054   invalid_arg:
2055     sqlite3_result_error (context,
2056 			  "SQL/MM Spatial exception - invalid argument.", -1);
2057     return;
2058 }
2059 
2060 static int
check_matching_topo_net(struct gaia_network * net,struct gaia_topology * topo)2061 check_matching_topo_net (struct gaia_network *net, struct gaia_topology *topo)
2062 {
2063 /* checking for matching SRID and DIMs */
2064     if (net->srid != topo->srid)
2065 	return 0;
2066     if (net->has_z != topo->has_z)
2067 	return 0;
2068     return 1;
2069 }
2070 
2071 static int
do_spatnet_from_tgeo(struct gaia_network * net,struct gaia_topology * topo)2072 do_spatnet_from_tgeo (struct gaia_network *net, struct gaia_topology *topo)
2073 {
2074 /* populating a Spatial Network starting from an existing Topology */
2075     char *table;
2076     char *xtable1;
2077     char *xtable2;
2078     char *sql;
2079     char *errMsg;
2080     int ret;
2081 
2082 /* preparing the SQL statement - NODE */
2083     table = sqlite3_mprintf ("%s_node", net->network_name);
2084     xtable1 = gaiaDoubleQuotedSql (table);
2085     sqlite3_free (table);
2086     table = sqlite3_mprintf ("%s_node", topo->topology_name);
2087     xtable2 = gaiaDoubleQuotedSql (table);
2088     sqlite3_free (table);
2089     sql = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (node_id, geometry) "
2090 			   "SELECT node_id, geom FROM MAIN.\"%s\"", xtable1,
2091 			   xtable2);
2092     free (xtable1);
2093     free (xtable2);
2094     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
2095     sqlite3_free (sql);
2096     if (ret != SQLITE_OK)
2097       {
2098 	  char *msg =
2099 	      sqlite3_mprintf ("ST_SpatNetFromTGeo() error: \"%s\"", errMsg);
2100 	  sqlite3_free (errMsg);
2101 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2102 	  sqlite3_free (msg);
2103 	  return 0;
2104       }
2105 
2106 /* preparing the SQL statement - LINK */
2107     table = sqlite3_mprintf ("%s_link", net->network_name);
2108     xtable1 = gaiaDoubleQuotedSql (table);
2109     sqlite3_free (table);
2110     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
2111     xtable2 = gaiaDoubleQuotedSql (table);
2112     sqlite3_free (table);
2113     sql =
2114 	sqlite3_mprintf
2115 	("INSERT INTO MAIN.\"%s\" (link_id, start_node, end_node, geometry) "
2116 	 "SELECT edge_id, start_node, end_node, geom FROM MAIN.\"%s\"", xtable1,
2117 	 xtable2);
2118     free (xtable1);
2119     free (xtable2);
2120     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
2121     sqlite3_free (sql);
2122     if (ret != SQLITE_OK)
2123       {
2124 	  char *msg =
2125 	      sqlite3_mprintf ("ST_SpatNetFromTGeo() error: \"%s\"", errMsg);
2126 	  sqlite3_free (errMsg);
2127 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2128 	  sqlite3_free (msg);
2129 	  return 0;
2130       }
2131 
2132     return 1;
2133 }
2134 
2135 SPATIALITE_PRIVATE void
fnctaux_SpatNetFromTGeo(const void * xcontext,int argc,const void * xargv)2136 fnctaux_SpatNetFromTGeo (const void *xcontext, int argc, const void *xargv)
2137 {
2138 /* SQL function:
2139 / ST_SpatNetFromTGeo ( text network-name, text topology-name )
2140 /
2141 / returns: 1 on success
2142 / raises an exception on failure
2143 */
2144     int ret;
2145     const char *network_name;
2146     const char *topo_name;
2147     GaiaNetworkAccessorPtr accessor;
2148     struct gaia_network *net;
2149     GaiaTopologyAccessorPtr accessor2;
2150     struct gaia_topology *topo;
2151     sqlite3_context *context = (sqlite3_context *) xcontext;
2152     sqlite3_value **argv = (sqlite3_value **) xargv;
2153     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2154     struct splite_internal_cache *cache = sqlite3_user_data (context);
2155     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2156     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2157 	goto null_arg;
2158     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2159 	network_name = (const char *) sqlite3_value_text (argv[0]);
2160     else
2161 	goto invalid_arg;
2162     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2163 	goto null_arg;
2164     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
2165 	topo_name = (const char *) sqlite3_value_text (argv[1]);
2166     else
2167 	goto invalid_arg;
2168 
2169 /* attempting to get a Network Accessor */
2170     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2171     if (accessor == NULL)
2172 	goto no_net;
2173     net = (struct gaia_network *) accessor;
2174     if (net->spatial == 0)
2175 	goto logical_err;
2176     if (!check_empty_network (net))
2177 	goto non_empty;
2178 
2179 /* attempting to get a Topology Accessor */
2180     accessor2 = gaiaGetTopology (sqlite, cache, topo_name);
2181     if (accessor2 == NULL)
2182 	goto no_topo;
2183     topo = (struct gaia_topology *) accessor2;
2184     if (!check_matching_topo_net (net, topo))
2185 	goto mismatching;
2186 
2187     gaianet_reset_last_error_msg (accessor);
2188     start_net_savepoint (sqlite, cache);
2189     ret = do_spatnet_from_tgeo (net, topo);
2190     if (ret <= 0)
2191 	rollback_net_savepoint (sqlite, cache);
2192     else
2193 	release_net_savepoint (sqlite, cache);
2194     if (ret <= 0)
2195       {
2196 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2197 	  gaianet_set_last_error_msg (accessor, msg);
2198 	  sqlite3_result_error (context, msg, -1);
2199 	  return;
2200       }
2201     sqlite3_result_int (context, 1);
2202     return;
2203 
2204   no_net:
2205     sqlite3_result_error (context,
2206 			  "SQL/MM Spatial exception - invalid network name.",
2207 			  -1);
2208     return;
2209 
2210   logical_err:
2211     sqlite3_result_error (context,
2212 			  "ST_SpatNetFromTGeo() cannot be applied to Logical Network.",
2213 			  -1);
2214     return;
2215 
2216   non_empty:
2217     sqlite3_result_error (context,
2218 			  "SQL/MM Spatial exception - non-empty network.", -1);
2219     return;
2220 
2221   no_topo:
2222     sqlite3_result_error (context,
2223 			  "SQL/MM Spatial exception - invalid topology name.",
2224 			  -1);
2225     return;
2226 
2227   null_arg:
2228     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2229 			  -1);
2230     return;
2231 
2232   invalid_arg:
2233     sqlite3_result_error (context,
2234 			  "SQL/MM Spatial exception - invalid argument.", -1);
2235     return;
2236 
2237   mismatching:
2238     sqlite3_result_error (context,
2239 			  "SQL/MM Spatial exception - mismatching SRID or dimensions.",
2240 			  -1);
2241     return;
2242 }
2243 
2244 SPATIALITE_PRIVATE void
fnctaux_ValidLogicalNet(const void * xcontext,int argc,const void * xargv)2245 fnctaux_ValidLogicalNet (const void *xcontext, int argc, const void *xargv)
2246 {
2247 /* SQL function:
2248 / ST_ValidLogicalNet ( text network-name )
2249 /
2250 / create/update a table containing an validation report for a given
2251 / Logical Network
2252 /
2253 / returns NULL on success
2254 / raises an exception on failure
2255 */
2256     const char *network_name;
2257     int ret;
2258     GaiaNetworkAccessorPtr accessor;
2259     struct gaia_network *net;
2260     sqlite3_context *context = (sqlite3_context *) xcontext;
2261     sqlite3_value **argv = (sqlite3_value **) xargv;
2262     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2263     struct splite_internal_cache *cache = sqlite3_user_data (context);
2264     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2265     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2266 	goto null_arg;
2267     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2268 	network_name = (const char *) sqlite3_value_text (argv[0]);
2269     else
2270 	goto invalid_arg;
2271 
2272 /* attempting to get a Network Accessor */
2273     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2274     if (accessor == NULL)
2275 	goto no_net;
2276     net = (struct gaia_network *) accessor;
2277     if (net->spatial)
2278 	goto spatial_err;
2279     if (check_empty_network (net))
2280 	goto empty;
2281 
2282     gaianet_reset_last_error_msg (accessor);
2283     start_net_savepoint (sqlite, cache);
2284     ret = gaiaValidLogicalNet (accessor);
2285     if (!ret)
2286 	rollback_net_savepoint (sqlite, cache);
2287     else
2288 	release_net_savepoint (sqlite, cache);
2289     if (!ret)
2290       {
2291 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2292 	  gaianet_set_last_error_msg (accessor, msg);
2293 	  sqlite3_result_error (context, msg, -1);
2294 	  return;
2295       }
2296     sqlite3_result_null (context);
2297     return;
2298 
2299   no_net:
2300     sqlite3_result_error (context,
2301 			  "SQL/MM Spatial exception - invalid network name.",
2302 			  -1);
2303     return;
2304 
2305   spatial_err:
2306     sqlite3_result_error (context,
2307 			  "ST_ValidLogicalNet() cannot be applied to Spatial Network.",
2308 			  -1);
2309     return;
2310 
2311   null_arg:
2312     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2313 			  -1);
2314     return;
2315 
2316   invalid_arg:
2317     sqlite3_result_error (context,
2318 			  "SQL/MM Spatial exception - invalid argument.", -1);
2319     return;
2320 
2321   empty:
2322     sqlite3_result_error (context,
2323 			  "SQL/MM Spatial exception - empty network.", -1);
2324     return;
2325 }
2326 
2327 SPATIALITE_PRIVATE void
fnctaux_ValidSpatialNet(const void * xcontext,int argc,const void * xargv)2328 fnctaux_ValidSpatialNet (const void *xcontext, int argc, const void *xargv)
2329 {
2330 /* SQL function:
2331 / ST_ValidSpatialNet ( text network-name )
2332 /
2333 / create/update a table containing an validation report for a given
2334 / Spatial Network
2335 /
2336 / returns NULL on success
2337 / raises an exception on failure
2338 */
2339     const char *network_name;
2340     int ret;
2341     GaiaNetworkAccessorPtr accessor;
2342     struct gaia_network *net;
2343     sqlite3_context *context = (sqlite3_context *) xcontext;
2344     sqlite3_value **argv = (sqlite3_value **) xargv;
2345     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2346     struct splite_internal_cache *cache = sqlite3_user_data (context);
2347     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2348     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2349 	goto null_arg;
2350     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2351 	network_name = (const char *) sqlite3_value_text (argv[0]);
2352     else
2353 	goto invalid_arg;
2354 
2355 /* attempting to get a Network Accessor */
2356     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2357     if (accessor == NULL)
2358 	goto no_net;
2359     net = (struct gaia_network *) accessor;
2360     if (net->spatial == 0)
2361 	goto logical_err;
2362     if (check_empty_network (net))
2363 	goto empty;
2364 
2365     gaianet_reset_last_error_msg (accessor);
2366     start_net_savepoint (sqlite, cache);
2367     ret = gaiaValidSpatialNet (accessor);
2368     if (!ret)
2369 	rollback_net_savepoint (sqlite, cache);
2370     else
2371 	release_net_savepoint (sqlite, cache);
2372     if (!ret)
2373       {
2374 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2375 	  gaianet_set_last_error_msg (accessor, msg);
2376 	  sqlite3_result_error (context, msg, -1);
2377 	  return;
2378       }
2379     sqlite3_result_null (context);
2380     return;
2381 
2382   no_net:
2383     sqlite3_result_error (context,
2384 			  "SQL/MM Spatial exception - invalid network name.",
2385 			  -1);
2386     return;
2387 
2388   logical_err:
2389     sqlite3_result_error (context,
2390 			  "ST_ValidSpatialNet() cannot be applied to Logical Network.",
2391 			  -1);
2392     return;
2393 
2394   null_arg:
2395     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2396 			  -1);
2397     return;
2398 
2399   invalid_arg:
2400     sqlite3_result_error (context,
2401 			  "SQL/MM Spatial exception - invalid argument.", -1);
2402     return;
2403 
2404   empty:
2405     sqlite3_result_error (context,
2406 			  "SQL/MM Spatial exception - empty network.", -1);
2407     return;
2408 }
2409 
2410 SPATIALITE_PRIVATE void
fnctaux_SpatNetFromGeom(const void * xcontext,int argc,const void * xargv)2411 fnctaux_SpatNetFromGeom (const void *xcontext, int argc, const void *xargv)
2412 {
2413 /* SQL function:
2414 / ST_SpatNetFromGeom ( text network-name , blob geom-collection )
2415 /
2416 / creates and populates an empty Network by importing a Geometry-collection
2417 /
2418 / returns NULL on success
2419 / raises an exception on failure
2420 */
2421     const char *network_name;
2422     int ret;
2423     const unsigned char *blob;
2424     int blob_sz;
2425     gaiaGeomCollPtr geom = NULL;
2426     int gpkg_amphibious = 0;
2427     int gpkg_mode = 0;
2428     GaiaNetworkAccessorPtr accessor;
2429     struct gaia_network *net;
2430     sqlite3_context *context = (sqlite3_context *) xcontext;
2431     sqlite3_value **argv = (sqlite3_value **) xargv;
2432     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2433     struct splite_internal_cache *cache = sqlite3_user_data (context);
2434     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2435     if (cache != NULL)
2436       {
2437 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2438 	  gpkg_mode = cache->gpkg_mode;
2439       }
2440     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2441 	goto null_arg;
2442     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2443 	network_name = (const char *) sqlite3_value_text (argv[0]);
2444     else
2445 	goto invalid_arg;
2446     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2447 	goto null_arg;
2448     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2449       {
2450 	  blob = sqlite3_value_blob (argv[1]);
2451 	  blob_sz = sqlite3_value_bytes (argv[1]);
2452 	  geom =
2453 	      gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
2454 					   gpkg_amphibious);
2455       }
2456     else
2457 	goto invalid_arg;
2458     if (geom == NULL)
2459 	goto not_geom;
2460 
2461 /* attempting to get a Network Accessor */
2462     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2463     if (accessor == NULL)
2464 	goto no_net;
2465     net = (struct gaia_network *) accessor;
2466     if (net->spatial == 0)
2467 	goto logical_err;
2468     if (!check_empty_network (net))
2469 	goto not_empty;
2470     if (!check_matching_srid_dims (accessor, geom->Srid, geom->DimensionModel))
2471 	goto invalid_geom;
2472 
2473     gaianet_reset_last_error_msg (accessor);
2474     start_net_savepoint (sqlite, cache);
2475     ret = auxnet_insert_into_network (accessor, geom);
2476     if (!ret)
2477 	rollback_net_savepoint (sqlite, cache);
2478     else
2479 	release_net_savepoint (sqlite, cache);
2480     if (!ret)
2481       {
2482 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2483 	  gaianet_set_last_error_msg (accessor, msg);
2484 	  sqlite3_result_error (context, msg, -1);
2485 	  return;
2486       }
2487     sqlite3_result_null (context);
2488     gaiaFreeGeomColl (geom);
2489     return;
2490 
2491   no_net:
2492     if (geom != NULL)
2493 	gaiaFreeGeomColl (geom);
2494     sqlite3_result_error (context,
2495 			  "SQL/MM Spatial exception - invalid network name.",
2496 			  -1);
2497     return;
2498 
2499   logical_err:
2500     sqlite3_result_error (context,
2501 			  "ST_ValidSpatialNet() cannot be applied to Logical Network.",
2502 			  -1);
2503     return;
2504 
2505   null_arg:
2506     if (geom != NULL)
2507 	gaiaFreeGeomColl (geom);
2508     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2509 			  -1);
2510     return;
2511 
2512   invalid_arg:
2513     if (geom != NULL)
2514 	gaiaFreeGeomColl (geom);
2515     sqlite3_result_error (context,
2516 			  "SQL/MM Spatial exception - invalid argument.", -1);
2517     return;
2518 
2519   not_empty:
2520     if (geom != NULL)
2521 	gaiaFreeGeomColl (geom);
2522     sqlite3_result_error (context,
2523 			  "SQL/MM Spatial exception - non-empty network.", -1);
2524     return;
2525 
2526   not_geom:
2527     if (geom != NULL)
2528 	gaiaFreeGeomColl (geom);
2529     sqlite3_result_error (context,
2530 			  "SQL/MM Spatial exception - not a Geometry.", -1);
2531     return;
2532 
2533   invalid_geom:
2534     if (geom != NULL)
2535 	gaiaFreeGeomColl (geom);
2536     sqlite3_result_error (context,
2537 			  "SQL/MM Spatial exception - invalid Geometry (mismatching SRID or dimensions).",
2538 			  -1);
2539     return;
2540 }
2541 
2542 SPATIALITE_PRIVATE void
fnctaux_GetNetNodeByPoint(const void * xcontext,int argc,const void * xargv)2543 fnctaux_GetNetNodeByPoint (const void *xcontext, int argc, const void *xargv)
2544 {
2545 /* SQL function:
2546 / GetNetNodeByPoint ( text network-name, Geometry point )
2547 / GetNetNodeByPoint ( text network-name, Geometry point, double tolerance )
2548 /
2549 / returns: the ID of some Node on success, 0 if no Node was found
2550 / raises an exception on failure
2551 */
2552     sqlite3_int64 ret;
2553     const char *network_name;
2554     unsigned char *p_blob;
2555     int n_bytes;
2556     gaiaGeomCollPtr point = NULL;
2557     gaiaPointPtr pt;
2558     double tolerance = 0.0;
2559     int invalid = 0;
2560     GaiaNetworkAccessorPtr accessor;
2561     struct gaia_network *net;
2562     int gpkg_amphibious = 0;
2563     int gpkg_mode = 0;
2564     sqlite3_context *context = (sqlite3_context *) xcontext;
2565     sqlite3_value **argv = (sqlite3_value **) xargv;
2566     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2567     struct splite_internal_cache *cache = sqlite3_user_data (context);
2568     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2569     if (cache != NULL)
2570       {
2571 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2572 	  gpkg_mode = cache->gpkg_mode;
2573       }
2574     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2575 	goto null_arg;
2576     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2577 	network_name = (const char *) sqlite3_value_text (argv[0]);
2578     else
2579 	goto invalid_arg;
2580     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2581 	goto null_arg;
2582     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2583       {
2584 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
2585 	  n_bytes = sqlite3_value_bytes (argv[1]);
2586       }
2587     else
2588 	goto invalid_arg;
2589     if (argc >= 3)
2590       {
2591 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2592 	      goto null_arg;
2593 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2594 	    {
2595 		int t = sqlite3_value_int (argv[2]);
2596 		tolerance = t;
2597 	    }
2598 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
2599 	      tolerance = sqlite3_value_double (argv[2]);
2600 	  else
2601 	      goto invalid_arg;
2602 	  if (tolerance < 0.0)
2603 	      goto negative_tolerance;
2604       }
2605 
2606 /* attempting to get a Point Geometry */
2607     point =
2608 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
2609 				     gpkg_amphibious);
2610     if (!point)
2611 	goto invalid_arg;
2612     if (point->FirstLinestring != NULL)
2613 	invalid = 1;
2614     if (point->FirstPolygon != NULL)
2615 	invalid = 1;
2616     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
2617 	invalid = 1;
2618     if (invalid)
2619 	goto invalid_arg;
2620 
2621 /* attempting to get a Network Accessor */
2622     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2623     if (accessor == NULL)
2624 	goto no_net;
2625     net = (struct gaia_network *) accessor;
2626     if (net->spatial == 0)
2627 	goto logical_err;
2628     pt = point->FirstPoint;
2629 
2630     gaianet_reset_last_error_msg (accessor);
2631     start_net_savepoint (sqlite, cache);
2632     ret = gaiaGetNetNodeByPoint (accessor, pt, tolerance);
2633     if (ret < 0)
2634 	rollback_net_savepoint (sqlite, cache);
2635     else
2636 	release_net_savepoint (sqlite, cache);
2637     gaiaFreeGeomColl (point);
2638     point = NULL;
2639     if (ret < 0)
2640       {
2641 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2642 	  gaianet_set_last_error_msg (accessor, msg);
2643 	  sqlite3_result_error (context, msg, -1);
2644 	  return;
2645       }
2646     sqlite3_result_int64 (context, ret);
2647     return;
2648 
2649   no_net:
2650     if (point != NULL)
2651 	gaiaFreeGeomColl (point);
2652     sqlite3_result_error (context,
2653 			  "SQL/MM Spatial exception - invalid network name.",
2654 			  -1);
2655     return;
2656 
2657   null_arg:
2658     if (point != NULL)
2659 	gaiaFreeGeomColl (point);
2660     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2661 			  -1);
2662     return;
2663 
2664   invalid_arg:
2665     if (point != NULL)
2666 	gaiaFreeGeomColl (point);
2667     sqlite3_result_error (context,
2668 			  "SQL/MM Spatial exception - invalid argument.", -1);
2669     return;
2670 
2671   logical_err:
2672     if (point != NULL)
2673 	gaiaFreeGeomColl (point);
2674     sqlite3_result_error (context,
2675 			  "GetNetNodekByPoint() cannot be applied to Logical Network.",
2676 			  -1);
2677     return;
2678 
2679   negative_tolerance:
2680     if (point != NULL)
2681 	gaiaFreeGeomColl (point);
2682     sqlite3_result_error (context,
2683 			  "SQL/MM Spatial exception - illegal negative tolerance.",
2684 			  -1);
2685     return;
2686 }
2687 
2688 SPATIALITE_PRIVATE void
fnctaux_GetLinkByPoint(const void * xcontext,int argc,const void * xargv)2689 fnctaux_GetLinkByPoint (const void *xcontext, int argc, const void *xargv)
2690 {
2691 /* SQL function:
2692 / GetLinkByPoint ( text network-name, Geometry point )
2693 / GetLinkByPoint ( text network-name, Geometry point, double tolerance )
2694 /
2695 / returns: the ID of some Link on success
2696 / raises an exception on failure
2697 */
2698     sqlite3_int64 ret;
2699     const char *network_name;
2700     unsigned char *p_blob;
2701     int n_bytes;
2702     gaiaGeomCollPtr point = NULL;
2703     gaiaPointPtr pt;
2704     double tolerance = 0;
2705     int invalid = 0;
2706     GaiaNetworkAccessorPtr accessor;
2707     struct gaia_network *net;
2708     int gpkg_amphibious = 0;
2709     int gpkg_mode = 0;
2710     sqlite3_context *context = (sqlite3_context *) xcontext;
2711     sqlite3_value **argv = (sqlite3_value **) xargv;
2712     sqlite3 *sqlite = sqlite3_context_db_handle (context);
2713     struct splite_internal_cache *cache = sqlite3_user_data (context);
2714     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
2715     if (cache != NULL)
2716       {
2717 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2718 	  gpkg_mode = cache->gpkg_mode;
2719       }
2720     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
2721 	goto null_arg;
2722     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
2723 	network_name = (const char *) sqlite3_value_text (argv[0]);
2724     else
2725 	goto invalid_arg;
2726     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
2727 	goto null_arg;
2728     else if (sqlite3_value_type (argv[1]) == SQLITE_BLOB)
2729       {
2730 	  p_blob = (unsigned char *) sqlite3_value_blob (argv[1]);
2731 	  n_bytes = sqlite3_value_bytes (argv[1]);
2732       }
2733     else
2734 	goto invalid_arg;
2735     if (argc >= 3)
2736       {
2737 	  if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
2738 	      goto null_arg;
2739 	  else if (sqlite3_value_type (argv[2]) == SQLITE_INTEGER)
2740 	    {
2741 		int t = sqlite3_value_int (argv[2]);
2742 		tolerance = t;
2743 	    }
2744 	  else if (sqlite3_value_type (argv[2]) == SQLITE_FLOAT)
2745 	      tolerance = sqlite3_value_double (argv[2]);
2746 	  else
2747 	      goto invalid_arg;
2748 	  if (tolerance < 0.0)
2749 	      goto negative_tolerance;
2750       }
2751 
2752 /* attempting to get a Point Geometry */
2753     point =
2754 	gaiaFromSpatiaLiteBlobWkbEx (p_blob, n_bytes, gpkg_mode,
2755 				     gpkg_amphibious);
2756     if (!point)
2757 	goto invalid_arg;
2758     if (point->FirstLinestring != NULL)
2759 	invalid = 1;
2760     if (point->FirstPolygon != NULL)
2761 	invalid = 1;
2762     if (point->FirstPoint != point->LastPoint || point->FirstPoint == NULL)
2763 	invalid = 1;
2764     if (invalid)
2765 	goto invalid_arg;
2766 
2767 /* attempting to get a Network Accessor */
2768     accessor = gaiaGetNetwork (sqlite, cache, network_name);
2769     if (accessor == NULL)
2770 	goto no_net;
2771     net = (struct gaia_network *) accessor;
2772     if (net->spatial == 0)
2773 	goto logical_err;
2774     pt = point->FirstPoint;
2775 
2776     gaianet_reset_last_error_msg (accessor);
2777     start_net_savepoint (sqlite, cache);
2778     ret = gaiaGetLinkByPoint (accessor, pt, tolerance);
2779     if (ret < 0)
2780 	rollback_net_savepoint (sqlite, cache);
2781     else
2782 	release_net_savepoint (sqlite, cache);
2783     gaiaFreeGeomColl (point);
2784     point = NULL;
2785     if (ret < 0)
2786       {
2787 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
2788 	  gaianet_set_last_error_msg (accessor, msg);
2789 	  sqlite3_result_error (context, msg, -1);
2790 	  return;
2791       }
2792     sqlite3_result_int64 (context, ret);
2793     return;
2794 
2795   no_net:
2796     if (point != NULL)
2797 	gaiaFreeGeomColl (point);
2798     sqlite3_result_error (context,
2799 			  "SQL/MM Spatial exception - invalid network name.",
2800 			  -1);
2801     return;
2802 
2803   null_arg:
2804     if (point != NULL)
2805 	gaiaFreeGeomColl (point);
2806     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
2807 			  -1);
2808     return;
2809 
2810   invalid_arg:
2811     if (point != NULL)
2812 	gaiaFreeGeomColl (point);
2813     sqlite3_result_error (context,
2814 			  "SQL/MM Spatial exception - invalid argument.", -1);
2815     return;
2816 
2817   logical_err:
2818     if (point != NULL)
2819 	gaiaFreeGeomColl (point);
2820     sqlite3_result_error (context,
2821 			  "GetLinkByPoint() cannot be applied to Logical Network.",
2822 			  -1);
2823     return;
2824 
2825   negative_tolerance:
2826     if (point != NULL)
2827 	gaiaFreeGeomColl (point);
2828     sqlite3_result_error (context,
2829 			  "SQL/MM Spatial exception - illegal negative tolerance.",
2830 			  -1);
2831     return;
2832 }
2833 
2834 static int
check_matching_srid_dims_class(GaiaNetworkAccessorPtr accessor,int srid,int dims,int linear)2835 check_matching_srid_dims_class (GaiaNetworkAccessorPtr accessor, int srid,
2836 				int dims, int linear)
2837 {
2838 /* checking for matching SRID and DIMs */
2839     struct gaia_network *net = (struct gaia_network *) accessor;
2840     if (net->srid != srid)
2841 	return 0;
2842     if (!linear)
2843 	return 0;
2844     if (net->has_z)
2845       {
2846 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
2847 	      ;
2848 	  else
2849 	      return 0;
2850       }
2851     else
2852       {
2853 	  if (dims == GAIA_XY_Z || dims == GAIA_XY_Z_M)
2854 	      return 0;
2855       }
2856     return 1;
2857 }
2858 
2859 static int
check_input_geonet_table(sqlite3 * sqlite,const char * db_prefix,const char * table,const char * column,char ** xtable,char ** xcolumn,int * srid,int * dims,int * linear)2860 check_input_geonet_table (sqlite3 * sqlite, const char *db_prefix,
2861 			  const char *table, const char *column, char **xtable,
2862 			  char **xcolumn, int *srid, int *dims, int *linear)
2863 {
2864 /* checking if an input GeoTable do really exist */
2865     int ret;
2866     int i;
2867     char **results;
2868     int rows;
2869     int columns;
2870     char *errMsg = NULL;
2871     char *sql;
2872     char *xprefix;
2873     int len;
2874     int count = 0;
2875     char *xx_table = NULL;
2876     char *xx_column = NULL;
2877     char *ztable;
2878     int xtype;
2879     int xdims;
2880     int xsrid;
2881 
2882     *xtable = NULL;
2883     *xcolumn = NULL;
2884     *srid = -1;
2885     *dims = GAIA_XY;
2886     *linear = 1;
2887 
2888 /* querying GEOMETRY_COLUMNS */
2889     xprefix = gaiaDoubleQuotedSql (db_prefix);
2890     if (column == NULL)
2891 	sql =
2892 	    sqlite3_mprintf
2893 	    ("SELECT f_table_name, f_geometry_column, geometry_type, srid "
2894 	     "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q)",
2895 	     xprefix, table);
2896     else
2897 	sql =
2898 	    sqlite3_mprintf
2899 	    ("SELECT f_table_name, f_geometry_column, geometry_type, srid "
2900 	     "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q) AND "
2901 	     "Lower(f_geometry_column) = Lower(%Q)", xprefix, table, column);
2902     free (xprefix);
2903     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
2904     sqlite3_free (sql);
2905     if (ret != SQLITE_OK)
2906       {
2907 	  sqlite3_free (errMsg);
2908 	  return 0;
2909       }
2910     for (i = 1; i <= rows; i++)
2911       {
2912 	  const char *table_name = results[(i * columns) + 0];
2913 	  const char *column_name = results[(i * columns) + 1];
2914 	  xtype = atoi (results[(i * columns) + 2]);
2915 	  xsrid = atoi (results[(i * columns) + 3]);
2916 	  len = strlen (table_name);
2917 	  if (xx_table != NULL)
2918 	      free (xx_table);
2919 	  xx_table = malloc (len + 1);
2920 	  strcpy (xx_table, table_name);
2921 	  len = strlen (column_name);
2922 	  if (xx_column != NULL)
2923 	      free (xx_column);
2924 	  xx_column = malloc (len + 1);
2925 	  strcpy (xx_column, column_name);
2926 	  count++;
2927       }
2928     sqlite3_free_table (results);
2929 
2930     if (count != 1)
2931       {
2932 	  if (xx_table != NULL)
2933 	      free (xx_table);
2934 	  if (xx_column != NULL)
2935 	      free (xx_column);
2936 	  return 0;
2937       }
2938 
2939 /* testing if the GeoTable do really exist */
2940     count = 0;
2941     xprefix = gaiaDoubleQuotedSql (db_prefix);
2942     ztable = gaiaDoubleQuotedSql (xx_table);
2943     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, ztable);
2944     free (xprefix);
2945     free (ztable);
2946     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
2947     sqlite3_free (sql);
2948     if (ret != SQLITE_OK)
2949       {
2950 	  sqlite3_free (errMsg);
2951 	  return 0;
2952       }
2953     for (i = 1; i <= rows; i++)
2954       {
2955 	  const char *column_name = results[(i * columns) + 1];
2956 	  if (strcasecmp (column_name, xx_column) == 0)
2957 	      count++;
2958       }
2959     sqlite3_free_table (results);
2960 
2961     if (count != 1)
2962       {
2963 	  if (xx_table != NULL)
2964 	      free (xx_table);
2965 	  if (xx_column != NULL)
2966 	      free (xx_column);
2967 	  return 0;
2968       }
2969 
2970     switch (xtype)
2971       {
2972       case 2:
2973       case 5:
2974 	  xdims = GAIA_XY;
2975 	  break;
2976       case 1002:
2977       case 1005:
2978 	  xdims = GAIA_XY_Z;
2979 	  break;
2980       case 2002:
2981       case 2005:
2982 	  xdims = GAIA_XY_M;
2983 	  break;
2984       case 3002:
2985       case 3005:
2986 	  xdims = GAIA_XY_Z_M;
2987 	  break;
2988       default:
2989 	  *linear = 0;
2990 	  break;
2991       };
2992     *xtable = xx_table;
2993     *xcolumn = xx_column;
2994     *srid = xsrid;
2995     *dims = xdims;
2996     return 1;
2997 }
2998 
2999 SPATIALITE_PRIVATE void
fnctaux_TopoNet_FromGeoTable(const void * xcontext,int argc,const void * xargv)3000 fnctaux_TopoNet_FromGeoTable (const void *xcontext, int argc, const void *xargv)
3001 {
3002 /* SQL function:
3003 / TopoNet_FromGeoTable ( text network-name, text db-prefix, text table,
3004 /                        text column )
3005 /
3006 / returns: 1 on success
3007 / raises an exception on failure
3008 */
3009     int ret;
3010     const char *network_name;
3011     const char *db_prefix;
3012     const char *table;
3013     const char *column;
3014     char *xtable = NULL;
3015     char *xcolumn = NULL;
3016     int srid;
3017     int dims;
3018     int linear;
3019     GaiaNetworkAccessorPtr accessor;
3020     struct gaia_network *net;
3021     sqlite3_context *context = (sqlite3_context *) xcontext;
3022     sqlite3_value **argv = (sqlite3_value **) xargv;
3023     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3024     struct splite_internal_cache *cache = sqlite3_user_data (context);
3025     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3026     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3027 	goto null_arg;
3028     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3029 	network_name = (const char *) sqlite3_value_text (argv[0]);
3030     else
3031 	goto invalid_arg;
3032     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3033 	db_prefix = "main";
3034     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
3035 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
3036     else
3037 	goto invalid_arg;
3038     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
3039 	table = (const char *) sqlite3_value_text (argv[2]);
3040     else
3041 	goto invalid_arg;
3042     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
3043 	column = NULL;
3044     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
3045 	column = (const char *) sqlite3_value_text (argv[3]);
3046     else
3047 	goto invalid_arg;
3048 
3049 /* attempting to get a Network Accessor */
3050     accessor = gaiaGetNetwork (sqlite, cache, network_name);
3051     if (accessor == NULL)
3052 	goto no_net;
3053     net = (struct gaia_network *) accessor;
3054     if (net->spatial == 0)
3055 	goto logical_err;
3056 
3057 /* checking the input GeoTable */
3058     if (!check_input_geonet_table
3059 	(sqlite, db_prefix, table, column, &xtable, &xcolumn, &srid, &dims,
3060 	 &linear))
3061 	goto no_input;
3062     if (!check_matching_srid_dims_class (accessor, srid, dims, linear))
3063 	goto invalid_geom;
3064 
3065     gaianet_reset_last_error_msg (accessor);
3066     start_net_savepoint (sqlite, cache);
3067     ret = gaiaTopoNet_FromGeoTable (accessor, db_prefix, xtable, xcolumn);
3068     if (!ret)
3069 	rollback_net_savepoint (sqlite, cache);
3070     else
3071 	release_net_savepoint (sqlite, cache);
3072     free (xtable);
3073     free (xcolumn);
3074     if (!ret)
3075       {
3076 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
3077 	  gaianet_set_last_error_msg (accessor, msg);
3078 	  sqlite3_result_error (context, msg, -1);
3079 	  return;
3080       }
3081     sqlite3_result_int (context, 1);
3082     return;
3083 
3084   no_net:
3085     if (xtable != NULL)
3086 	free (xtable);
3087     if (xcolumn != NULL)
3088 	free (xcolumn);
3089     sqlite3_result_error (context,
3090 			  "SQL/MM Spatial exception - invalid network name.",
3091 			  -1);
3092     return;
3093 
3094   no_input:
3095     if (xtable != NULL)
3096 	free (xtable);
3097     if (xcolumn != NULL)
3098 	free (xcolumn);
3099     sqlite3_result_error (context,
3100 			  "SQL/MM Spatial exception - invalid input GeoTable.",
3101 			  -1);
3102     return;
3103 
3104   null_arg:
3105     if (xtable != NULL)
3106 	free (xtable);
3107     if (xcolumn != NULL)
3108 	free (xcolumn);
3109     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
3110 			  -1);
3111     return;
3112 
3113   invalid_arg:
3114     if (xtable != NULL)
3115 	free (xtable);
3116     if (xcolumn != NULL)
3117 	free (xcolumn);
3118     sqlite3_result_error (context,
3119 			  "SQL/MM Spatial exception - invalid argument.", -1);
3120     return;
3121 
3122   invalid_geom:
3123     if (xtable != NULL)
3124 	free (xtable);
3125     if (xcolumn != NULL)
3126 	free (xcolumn);
3127     sqlite3_result_error (context,
3128 			  "SQL/MM Spatial exception - invalid GeoTable (mismatching SRID, dimensions or class).",
3129 			  -1);
3130     return;
3131 
3132   logical_err:
3133     if (xtable != NULL)
3134 	free (xtable);
3135     if (xcolumn != NULL)
3136 	free (xcolumn);
3137     sqlite3_result_error (context,
3138 			  "FromGeoTable() cannot be applied to Logical Network.",
3139 			  -1);
3140     return;
3141 }
3142 
3143 static int
check_reference_geonet_table(sqlite3 * sqlite,const char * db_prefix,const char * table,const char * column,char ** xtable,char ** xcolumn,int * srid,int * linear)3144 check_reference_geonet_table (sqlite3 * sqlite, const char *db_prefix,
3145 			      const char *table, const char *column,
3146 			      char **xtable, char **xcolumn, int *srid,
3147 			      int *linear)
3148 {
3149     int dims;
3150     return check_input_geonet_table (sqlite, db_prefix, table, column, xtable,
3151 				     xcolumn, srid, &dims, linear);
3152 }
3153 
3154 static int
check_matching_srid_class(GaiaNetworkAccessorPtr accessor,int srid,int linear)3155 check_matching_srid_class (GaiaNetworkAccessorPtr accessor, int srid,
3156 			   int linear)
3157 {
3158 /* checking for matching SRID */
3159     struct gaia_network *net = (struct gaia_network *) accessor;
3160     if (net->srid != srid)
3161 	return 0;
3162     if (!linear)
3163 	return 0;
3164     return 1;
3165 }
3166 
3167 static int
check_output_geonet_table(sqlite3 * sqlite,const char * table)3168 check_output_geonet_table (sqlite3 * sqlite, const char *table)
3169 {
3170 /* checking if an output GeoTable do already exist */
3171     int ret;
3172     int i;
3173     char **results;
3174     int rows;
3175     int columns;
3176     char *errMsg = NULL;
3177     char *sql;
3178     int count = 0;
3179     char *ztable;
3180 
3181 /* querying GEOMETRY_COLUMNS */
3182     sql =
3183 	sqlite3_mprintf
3184 	("SELECT f_table_name, f_geometry_column "
3185 	 "FROM MAIN.geometry_columns WHERE Lower(f_table_name) = Lower(%Q)",
3186 	 table);
3187     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
3188     sqlite3_free (sql);
3189     if (ret != SQLITE_OK)
3190       {
3191 	  sqlite3_free (errMsg);
3192 	  return 0;
3193       }
3194     for (i = 1; i <= rows; i++)
3195 	count++;
3196     sqlite3_free_table (results);
3197 
3198     if (count != 0)
3199 	return 0;
3200 
3201 /* testing if the Table already exist */
3202     count = 0;
3203     ztable = gaiaDoubleQuotedSql (table);
3204     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", ztable);
3205     free (ztable);
3206     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
3207     sqlite3_free (sql);
3208     if (ret != SQLITE_OK)
3209       {
3210 	  sqlite3_free (errMsg);
3211 	  return 0;
3212       }
3213     for (i = 1; i <= rows; i++)
3214 	count++;
3215     sqlite3_free_table (results);
3216 
3217     if (count != 0)
3218 	return 0;
3219     return 1;
3220 }
3221 
3222 SPATIALITE_PRIVATE void
fnctaux_TopoNet_ToGeoTable(const void * xcontext,int argc,const void * xargv)3223 fnctaux_TopoNet_ToGeoTable (const void *xcontext, int argc, const void *xargv)
3224 {
3225 /* SQL function:
3226 / TopoNet_ToGeoTable ( text network-name, text db-prefix, text ref_table,
3227 /                      text ref_column, text out_table )
3228 / TopoNet_ToGeoTable ( text network-name, text db-prefix, text ref_table,
3229 /                      text ref_column, text out_table, int with_spatial_index )
3230 /
3231 / returns: 1 on success
3232 / raises an exception on failure
3233 */
3234     int ret;
3235     const char *network_name;
3236     const char *db_prefix;
3237     const char *ref_table;
3238     const char *ref_column;
3239     const char *out_table;
3240     int with_spatial_index = 0;
3241     char *xreftable = NULL;
3242     char *xrefcolumn = NULL;
3243     int srid;
3244     int linear;
3245     GaiaNetworkAccessorPtr accessor;
3246     struct gaia_network *net;
3247     sqlite3_context *context = (sqlite3_context *) xcontext;
3248     sqlite3_value **argv = (sqlite3_value **) xargv;
3249     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3250     struct splite_internal_cache *cache = sqlite3_user_data (context);
3251     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3252     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3253 	goto null_arg;
3254     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3255 	network_name = (const char *) sqlite3_value_text (argv[0]);
3256     else
3257 	goto invalid_arg;
3258     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3259 	db_prefix = "main";
3260     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
3261 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
3262     else
3263 	goto invalid_arg;
3264     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
3265 	ref_table = (const char *) sqlite3_value_text (argv[2]);
3266     else
3267 	goto invalid_arg;
3268     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
3269 	ref_column = NULL;
3270     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
3271 	ref_column = (const char *) sqlite3_value_text (argv[3]);
3272     else
3273 	goto invalid_arg;
3274     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
3275 	goto null_arg;
3276     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
3277 	out_table = (const char *) sqlite3_value_text (argv[4]);
3278     else
3279 	goto invalid_arg;
3280     if (argc >= 6)
3281       {
3282 	  if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
3283 	      goto null_arg;
3284 	  else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
3285 	      with_spatial_index = sqlite3_value_int (argv[5]);
3286 	  else
3287 	      goto invalid_arg;
3288       }
3289 
3290 /* attempting to get a Network Accessor */
3291     accessor = gaiaGetNetwork (sqlite, cache, network_name);
3292     if (accessor == NULL)
3293 	goto no_net;
3294     net = (struct gaia_network *) accessor;
3295     if (net->spatial == 0)
3296 	goto logical_err;
3297 
3298 /* checking the reference GeoTable */
3299     if (!check_reference_geonet_table
3300 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
3301 	 &srid, &linear))
3302 	goto no_reference;
3303     if (!check_matching_srid_class (accessor, srid, linear))
3304 	goto invalid_geom;
3305 
3306 /* checking the output GeoTable */
3307     if (!check_output_geonet_table (sqlite, out_table))
3308 	goto err_output;
3309 
3310     gaianet_reset_last_error_msg (accessor);
3311     start_net_savepoint (sqlite, cache);
3312     ret =
3313 	gaiaTopoNet_ToGeoTable (accessor, db_prefix, xreftable, xrefcolumn,
3314 				out_table, with_spatial_index);
3315     if (!ret)
3316 	rollback_net_savepoint (sqlite, cache);
3317     else
3318 	release_net_savepoint (sqlite, cache);
3319     free (xreftable);
3320     free (xrefcolumn);
3321     if (!ret)
3322       {
3323 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
3324 	  gaianet_set_last_error_msg (accessor, msg);
3325 	  sqlite3_result_error (context, msg, -1);
3326 	  return;
3327       }
3328     sqlite3_result_int (context, 1);
3329     return;
3330 
3331   no_net:
3332     if (xreftable != NULL)
3333 	free (xreftable);
3334     if (xrefcolumn != NULL)
3335 	free (xrefcolumn);
3336     sqlite3_result_error (context,
3337 			  "SQL/MM Spatial exception - invalid network name.",
3338 			  -1);
3339     return;
3340 
3341   no_reference:
3342     if (xreftable != NULL)
3343 	free (xreftable);
3344     if (xrefcolumn != NULL)
3345 	free (xrefcolumn);
3346     sqlite3_result_error (context,
3347 			  "TopoNet_ToGeoTable: invalid reference GeoTable.",
3348 			  -1);
3349     return;
3350 
3351   err_output:
3352     if (xreftable != NULL)
3353 	free (xreftable);
3354     if (xrefcolumn != NULL)
3355 	free (xrefcolumn);
3356     sqlite3_result_error (context,
3357 			  "TopoNet_ToGeoTable: output GeoTable already exists.",
3358 			  -1);
3359     return;
3360 
3361   null_arg:
3362     if (xreftable != NULL)
3363 	free (xreftable);
3364     if (xrefcolumn != NULL)
3365 	free (xrefcolumn);
3366     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
3367 			  -1);
3368     return;
3369 
3370   invalid_arg:
3371     if (xreftable != NULL)
3372 	free (xreftable);
3373     if (xrefcolumn != NULL)
3374 	free (xrefcolumn);
3375     sqlite3_result_error (context,
3376 			  "SQL/MM Spatial exception - invalid argument.", -1);
3377     return;
3378 
3379   invalid_geom:
3380     if (xreftable != NULL)
3381 	free (xreftable);
3382     if (xrefcolumn != NULL)
3383 	free (xrefcolumn);
3384     sqlite3_result_error (context,
3385 			  "SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID or class).",
3386 			  -1);
3387     return;
3388 
3389   logical_err:
3390     if (xreftable != NULL)
3391 	free (xreftable);
3392     if (xrefcolumn != NULL)
3393 	free (xrefcolumn);
3394     sqlite3_result_error (context,
3395 			  "TopoNet_ToGeoTable() cannot be applied to Logical Network.",
3396 			  -1);
3397     return;
3398 }
3399 
3400 SPATIALITE_PRIVATE void
fnctaux_TopoNet_ToGeoTableGeneralize(const void * xcontext,int argc,const void * xargv)3401 fnctaux_TopoNet_ToGeoTableGeneralize (const void *xcontext, int argc,
3402 				      const void *xargv)
3403 {
3404 /* SQL function:
3405 / TopoNet_ToGeoTableGeneralize ( text network-name, text db-prefix,
3406 /                                text ref_table, text ref_column,
3407 /                                text out_table, int tolerance,
3408 /                                int with_spatial_index )
3409 /
3410 / returns: 1 on success
3411 / raises an exception on failure
3412 */
3413     int ret;
3414     const char *network_name;
3415     const char *db_prefix;
3416     const char *ref_table;
3417     const char *ref_column;
3418     const char *out_table;
3419     double tolerance = 0.0;
3420     int with_spatial_index = 0;
3421     char *xreftable = NULL;
3422     char *xrefcolumn = NULL;
3423     int srid;
3424     int linear;
3425     GaiaNetworkAccessorPtr accessor;
3426     struct gaia_network *net;
3427     sqlite3_context *context = (sqlite3_context *) xcontext;
3428     sqlite3_value **argv = (sqlite3_value **) xargv;
3429     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3430     struct splite_internal_cache *cache = sqlite3_user_data (context);
3431     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3432     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3433 	goto null_arg;
3434     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3435 	network_name = (const char *) sqlite3_value_text (argv[0]);
3436     else
3437 	goto invalid_arg;
3438     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3439 	db_prefix = "main";
3440     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
3441 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
3442     else
3443 	goto invalid_arg;
3444     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
3445 	ref_table = (const char *) sqlite3_value_text (argv[2]);
3446     else
3447 	goto invalid_arg;
3448     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
3449 	ref_column = NULL;
3450     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
3451 	ref_column = (const char *) sqlite3_value_text (argv[3]);
3452     else
3453 	goto invalid_arg;
3454     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
3455 	goto null_arg;
3456     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
3457 	out_table = (const char *) sqlite3_value_text (argv[4]);
3458     else
3459 	goto invalid_arg;
3460     if (sqlite3_value_type (argv[5]) == SQLITE_NULL)
3461 	goto null_arg;
3462     else if (sqlite3_value_type (argv[5]) == SQLITE_INTEGER)
3463       {
3464 	  int val = sqlite3_value_int (argv[5]);
3465 	  tolerance = val;
3466       }
3467     else if (sqlite3_value_type (argv[5]) == SQLITE_FLOAT)
3468 	tolerance = sqlite3_value_double (argv[5]);
3469     else
3470 	goto invalid_arg;
3471     if (argc >= 7)
3472       {
3473 	  if (sqlite3_value_type (argv[6]) == SQLITE_NULL)
3474 	      goto null_arg;
3475 	  else if (sqlite3_value_type (argv[6]) == SQLITE_INTEGER)
3476 	      with_spatial_index = sqlite3_value_int (argv[6]);
3477 	  else
3478 	      goto invalid_arg;
3479       }
3480 
3481 /* attempting to get a Network Accessor */
3482     accessor = gaiaGetNetwork (sqlite, cache, network_name);
3483     if (accessor == NULL)
3484 	goto no_net;
3485     net = (struct gaia_network *) accessor;
3486     if (net->spatial == 0)
3487 	goto logical_err;
3488 
3489 /* checking the reference GeoTable */
3490     if (!check_reference_geonet_table
3491 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
3492 	 &srid, &linear))
3493 	goto no_reference;
3494     if (!check_matching_srid_class (accessor, srid, linear))
3495 	goto invalid_geom;
3496 
3497 /* checking the output GeoTable */
3498     if (!check_output_geonet_table (sqlite, out_table))
3499 	goto err_output;
3500 
3501     gaianet_reset_last_error_msg (accessor);
3502     start_net_savepoint (sqlite, cache);
3503     ret =
3504 	gaiaTopoNet_ToGeoTableGeneralize (accessor, db_prefix, xreftable,
3505 					  xrefcolumn, out_table, tolerance,
3506 					  with_spatial_index);
3507     if (!ret)
3508 	rollback_net_savepoint (sqlite, cache);
3509     else
3510 	release_net_savepoint (sqlite, cache);
3511     free (xreftable);
3512     free (xrefcolumn);
3513     if (!ret)
3514       {
3515 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
3516 	  gaianet_set_last_error_msg (accessor, msg);
3517 	  sqlite3_result_error (context, msg, -1);
3518 	  return;
3519       }
3520     sqlite3_result_int (context, 1);
3521     return;
3522 
3523   no_net:
3524     if (xreftable != NULL)
3525 	free (xreftable);
3526     if (xrefcolumn != NULL)
3527 	free (xrefcolumn);
3528     sqlite3_result_error (context,
3529 			  "SQL/MM Spatial exception - invalid network name.",
3530 			  -1);
3531     return;
3532 
3533   no_reference:
3534     if (xreftable != NULL)
3535 	free (xreftable);
3536     if (xrefcolumn != NULL)
3537 	free (xrefcolumn);
3538     sqlite3_result_error (context,
3539 			  "TopoNet_ToGeoTableGeneralize: invalid reference GeoTable.",
3540 			  -1);
3541     return;
3542 
3543   err_output:
3544     if (xreftable != NULL)
3545 	free (xreftable);
3546     if (xrefcolumn != NULL)
3547 	free (xrefcolumn);
3548     sqlite3_result_error (context,
3549 			  "TopoNet_ToGeoTableGeneralize: output GeoTable already exists.",
3550 			  -1);
3551     return;
3552 
3553   null_arg:
3554     if (xreftable != NULL)
3555 	free (xreftable);
3556     if (xrefcolumn != NULL)
3557 	free (xrefcolumn);
3558     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
3559 			  -1);
3560     return;
3561 
3562   invalid_arg:
3563     if (xreftable != NULL)
3564 	free (xreftable);
3565     if (xrefcolumn != NULL)
3566 	free (xrefcolumn);
3567     sqlite3_result_error (context,
3568 			  "SQL/MM Spatial exception - invalid argument.", -1);
3569     return;
3570 
3571   invalid_geom:
3572     if (xreftable != NULL)
3573 	free (xreftable);
3574     if (xrefcolumn != NULL)
3575 	free (xrefcolumn);
3576     sqlite3_result_error (context,
3577 			  "SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID or class).",
3578 			  -1);
3579     return;
3580 
3581   logical_err:
3582     if (xreftable != NULL)
3583 	free (xreftable);
3584     if (xrefcolumn != NULL)
3585 	free (xrefcolumn);
3586     sqlite3_result_error (context,
3587 			  "TopoNet_ToGeoTableGeneralize() cannot be applied to Logical Network.",
3588 			  -1);
3589     return;
3590 }
3591 
3592 static int
do_clone_netnode(const char * db_prefix,const char * in_network,struct gaia_network * net_out)3593 do_clone_netnode (const char *db_prefix, const char *in_network,
3594 		  struct gaia_network *net_out)
3595 {
3596 /* cloning NODE */
3597     char *sql;
3598     char *table;
3599     char *xprefix;
3600     char *xtable;
3601     sqlite3_stmt *stmt_in = NULL;
3602     sqlite3_stmt *stmt_out = NULL;
3603     int ret;
3604 
3605 /* preparing the input SQL statement */
3606     xprefix = gaiaDoubleQuotedSql (db_prefix);
3607     table = sqlite3_mprintf ("%s_node", in_network);
3608     xtable = gaiaDoubleQuotedSql (table);
3609     sqlite3_free (table);
3610     sql =
3611 	sqlite3_mprintf ("SELECT node_id, geometry FROM \"%s\".\"%s\"", xprefix,
3612 			 xtable);
3613     free (xprefix);
3614     free (xtable);
3615     ret =
3616 	sqlite3_prepare_v2 (net_out->db_handle, sql, strlen (sql), &stmt_in,
3617 			    NULL);
3618     sqlite3_free (sql);
3619     if (ret != SQLITE_OK)
3620       {
3621 	  spatialite_e ("SELECT FROM \"node\" error: \"%s\"",
3622 			sqlite3_errmsg (net_out->db_handle));
3623 	  goto error;
3624       }
3625 
3626 /* preparing the output SQL statement */
3627     table = sqlite3_mprintf ("%s_node", net_out->network_name);
3628     xtable = gaiaDoubleQuotedSql (table);
3629     sqlite3_free (table);
3630     sql = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (node_id, geometry) "
3631 			   "VALUES (?, ?)", xtable);
3632     free (xtable);
3633     ret =
3634 	sqlite3_prepare_v2 (net_out->db_handle, sql, strlen (sql), &stmt_out,
3635 			    NULL);
3636     sqlite3_free (sql);
3637     if (ret != SQLITE_OK)
3638       {
3639 	  spatialite_e ("INSERT INTO \"node\" error: \"%s\"",
3640 			sqlite3_errmsg (net_out->db_handle));
3641 	  goto error;
3642       }
3643 
3644     sqlite3_reset (stmt_in);
3645     sqlite3_clear_bindings (stmt_in);
3646     while (1)
3647       {
3648 	  /* scrolling the result set rows */
3649 	  ret = sqlite3_step (stmt_in);
3650 	  if (ret == SQLITE_DONE)
3651 	      break;		/* end of result set */
3652 	  if (ret == SQLITE_ROW)
3653 	    {
3654 		sqlite3_reset (stmt_out);
3655 		sqlite3_clear_bindings (stmt_out);
3656 		if (sqlite3_column_type (stmt_in, 0) == SQLITE_INTEGER)
3657 		    sqlite3_bind_int64 (stmt_out, 1,
3658 					sqlite3_column_int64 (stmt_in, 0));
3659 		else
3660 		    goto invalid_value;
3661 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_NULL)
3662 		    sqlite3_bind_null (stmt_out, 2);
3663 		else if (sqlite3_column_type (stmt_in, 1) == SQLITE_BLOB)
3664 		    sqlite3_bind_blob (stmt_out, 2,
3665 				       sqlite3_column_blob (stmt_in, 1),
3666 				       sqlite3_column_bytes (stmt_in, 1),
3667 				       SQLITE_STATIC);
3668 		else
3669 		    goto invalid_value;
3670 		/* inserting into the output table */
3671 		ret = sqlite3_step (stmt_out);
3672 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3673 		    ;
3674 		else
3675 		  {
3676 		      spatialite_e ("INSERT INTO \"node\" step error: \"%s\"",
3677 				    sqlite3_errmsg (net_out->db_handle));
3678 		      goto error;
3679 		  }
3680 	    }
3681 	  else
3682 	    {
3683 		spatialite_e ("SELECT FROM \"node\" step error: %s",
3684 			      sqlite3_errmsg (net_out->db_handle));
3685 		goto error;
3686 	    }
3687       }
3688 
3689     sqlite3_finalize (stmt_in);
3690     sqlite3_finalize (stmt_out);
3691     return 1;
3692 
3693   invalid_value:
3694     spatialite_e ("SELECT FROM \"node\": found an invalid value");
3695 
3696   error:
3697     if (stmt_in != NULL)
3698 	sqlite3_finalize (stmt_in);
3699     if (stmt_out != NULL)
3700 	sqlite3_finalize (stmt_out);
3701     return 0;
3702 }
3703 
3704 static int
do_clone_link(const char * db_prefix,const char * in_network,struct gaia_network * net_out)3705 do_clone_link (const char *db_prefix, const char *in_network,
3706 	       struct gaia_network *net_out)
3707 {
3708 /* cloning LINK */
3709     char *sql;
3710     char *table;
3711     char *xprefix;
3712     char *xtable;
3713     sqlite3_stmt *stmt_in = NULL;
3714     sqlite3_stmt *stmt_out = NULL;
3715     int ret;
3716 
3717 /* preparing the input SQL statement */
3718     xprefix = gaiaDoubleQuotedSql (db_prefix);
3719     table = sqlite3_mprintf ("%s_link", in_network);
3720     xtable = gaiaDoubleQuotedSql (table);
3721     sqlite3_free (table);
3722     sql =
3723 	sqlite3_mprintf
3724 	("SELECT link_id, start_node, end_node, geometry FROM \"%s\".\"%s\"",
3725 	 xprefix, xtable);
3726     free (xprefix);
3727     free (xtable);
3728     ret =
3729 	sqlite3_prepare_v2 (net_out->db_handle, sql, strlen (sql), &stmt_in,
3730 			    NULL);
3731     sqlite3_free (sql);
3732     if (ret != SQLITE_OK)
3733       {
3734 	  spatialite_e ("SELECT FROM \"link\" error: \"%s\"",
3735 			sqlite3_errmsg (net_out->db_handle));
3736 	  goto error;
3737       }
3738 
3739 /* preparing the output SQL statement */
3740     table = sqlite3_mprintf ("%s_link", net_out->network_name);
3741     xtable = gaiaDoubleQuotedSql (table);
3742     sqlite3_free (table);
3743     sql =
3744 	sqlite3_mprintf
3745 	("INSERT INTO MAIN.\"%s\" (link_id, start_node, end_node, "
3746 	 "geometry) VALUES (?, ?, ?, ?)", xtable);
3747     free (xtable);
3748     ret =
3749 	sqlite3_prepare_v2 (net_out->db_handle, sql, strlen (sql), &stmt_out,
3750 			    NULL);
3751     sqlite3_free (sql);
3752     if (ret != SQLITE_OK)
3753       {
3754 	  spatialite_e ("INSERT INTO \"link\" error: \"%s\"",
3755 			sqlite3_errmsg (net_out->db_handle));
3756 	  goto error;
3757       }
3758 
3759     sqlite3_reset (stmt_in);
3760     sqlite3_clear_bindings (stmt_in);
3761     while (1)
3762       {
3763 	  /* scrolling the result set rows */
3764 	  ret = sqlite3_step (stmt_in);
3765 	  if (ret == SQLITE_DONE)
3766 	      break;		/* end of result set */
3767 	  if (ret == SQLITE_ROW)
3768 	    {
3769 		sqlite3_reset (stmt_out);
3770 		sqlite3_clear_bindings (stmt_out);
3771 		if (sqlite3_column_type (stmt_in, 0) == SQLITE_INTEGER)
3772 		    sqlite3_bind_int64 (stmt_out, 1,
3773 					sqlite3_column_int64 (stmt_in, 0));
3774 		else
3775 		    goto invalid_value;
3776 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_INTEGER)
3777 		    sqlite3_bind_int64 (stmt_out, 2,
3778 					sqlite3_column_int64 (stmt_in, 1));
3779 		else
3780 		    goto invalid_value;
3781 		if (sqlite3_column_type (stmt_in, 2) == SQLITE_INTEGER)
3782 		    sqlite3_bind_int64 (stmt_out, 3,
3783 					sqlite3_column_int64 (stmt_in, 2));
3784 		else
3785 		    goto invalid_value;
3786 		if (sqlite3_column_type (stmt_in, 3) == SQLITE_NULL)
3787 		    sqlite3_bind_null (stmt_out, 4);
3788 		else if (sqlite3_column_type (stmt_in, 3) == SQLITE_BLOB)
3789 		    sqlite3_bind_blob (stmt_out, 4,
3790 				       sqlite3_column_blob (stmt_in, 3),
3791 				       sqlite3_column_bytes (stmt_in, 3),
3792 				       SQLITE_STATIC);
3793 		else
3794 		    goto invalid_value;
3795 		/* inserting into the output table */
3796 		ret = sqlite3_step (stmt_out);
3797 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3798 		    ;
3799 		else
3800 		  {
3801 		      spatialite_e ("INSERT INTO \"link\" step error: \"%s\"",
3802 				    sqlite3_errmsg (net_out->db_handle));
3803 		      goto error;
3804 		  }
3805 	    }
3806 	  else
3807 	    {
3808 		spatialite_e ("SELECT FROM \"link\" step error: %s",
3809 			      sqlite3_errmsg (net_out->db_handle));
3810 		goto error;
3811 	    }
3812       }
3813 
3814     sqlite3_finalize (stmt_in);
3815     sqlite3_finalize (stmt_out);
3816     return 1;
3817 
3818   invalid_value:
3819     spatialite_e ("SELECT FROM \"link\": found an invalid value");
3820 
3821   error:
3822     if (stmt_in != NULL)
3823 	sqlite3_finalize (stmt_in);
3824     if (stmt_out != NULL)
3825 	sqlite3_finalize (stmt_out);
3826     return 0;
3827 }
3828 
3829 static int
do_clone_network(const char * db_prefix,const char * in_network,GaiaNetworkAccessorPtr accessor)3830 do_clone_network (const char *db_prefix, const char *in_network,
3831 		  GaiaNetworkAccessorPtr accessor)
3832 {
3833 /* cloning a full Network */
3834     struct gaia_network *net_out = (struct gaia_network *) accessor;
3835 
3836 /* cloning NODE */
3837     if (!do_clone_netnode (db_prefix, in_network, net_out))
3838 	return 0;
3839 
3840 /* cloning LINK */
3841     if (!do_clone_link (db_prefix, in_network, net_out))
3842 	return 0;
3843 
3844     return 1;
3845 }
3846 
3847 static char *
gaiaGetAttachedNetwork(sqlite3 * handle,const char * db_prefix,const char * network_name,int * spatial,int * srid,int * has_z,int * allow_coincident)3848 gaiaGetAttachedNetwork (sqlite3 * handle, const char *db_prefix,
3849 			const char *network_name, int *spatial, int *srid,
3850 			int *has_z, int *allow_coincident)
3851 {
3852 /* attempting to retrieve the Input Network for TopoNet_Clone */
3853     char *sql;
3854     int ret;
3855     sqlite3_stmt *stmt = NULL;
3856     int ok = 0;
3857     char *xprefix;
3858     char *xnetwork_name = NULL;
3859     int xspatial;
3860     int xsrid;
3861     int xhas_z;
3862     int xallow_coincident;
3863 
3864 /* preparing the SQL query */
3865     xprefix = gaiaDoubleQuotedSql (db_prefix);
3866     sql =
3867 	sqlite3_mprintf
3868 	("SELECT network_name, spatial, srid, has_z, allow_coincident "
3869 	 "FROM \"%s\".networks WHERE Lower(network_name) = Lower(%Q)", xprefix,
3870 	 network_name);
3871     free (xprefix);
3872     ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
3873     sqlite3_free (sql);
3874     if (ret != SQLITE_OK)
3875       {
3876 	  spatialite_e ("SELECT FROM networks error: \"%s\"\n",
3877 			sqlite3_errmsg (handle));
3878 	  return NULL;
3879       }
3880 
3881     while (1)
3882       {
3883 	  /* scrolling the result set rows */
3884 	  ret = sqlite3_step (stmt);
3885 	  if (ret == SQLITE_DONE)
3886 	      break;		/* end of result set */
3887 	  if (ret == SQLITE_ROW)
3888 	    {
3889 		int ok_name = 0;
3890 		int ok_srid = 0;
3891 		int ok_z = 0;
3892 		int ok_spatial = 0;
3893 		int ok_allow_coincident = 0;
3894 		if (sqlite3_column_type (stmt, 0) == SQLITE_TEXT)
3895 		  {
3896 		      const char *str =
3897 			  (const char *) sqlite3_column_text (stmt, 0);
3898 		      if (xnetwork_name != NULL)
3899 			  free (xnetwork_name);
3900 		      xnetwork_name = malloc (strlen (str) + 1);
3901 		      strcpy (xnetwork_name, str);
3902 		      ok_name = 1;
3903 		  }
3904 		if (sqlite3_column_type (stmt, 1) == SQLITE_INTEGER)
3905 		  {
3906 		      xspatial = sqlite3_column_int (stmt, 1);
3907 		      ok_spatial = 1;
3908 		  }
3909 		if (sqlite3_column_type (stmt, 2) == SQLITE_INTEGER)
3910 		  {
3911 		      xsrid = sqlite3_column_int (stmt, 2);
3912 		      ok_srid = 1;
3913 		  }
3914 		if (sqlite3_column_type (stmt, 3) == SQLITE_INTEGER)
3915 		  {
3916 		      xhas_z = sqlite3_column_int (stmt, 3);
3917 		      ok_z = 1;
3918 		  }
3919 		if (sqlite3_column_type (stmt, 4) == SQLITE_INTEGER)
3920 		  {
3921 		      xallow_coincident = sqlite3_column_int (stmt, 4);
3922 		      ok_allow_coincident = 1;
3923 		  }
3924 		if (ok_name && ok_spatial && ok_srid && ok_z
3925 		    && ok_allow_coincident)
3926 		  {
3927 		      ok = 1;
3928 		      break;
3929 		  }
3930 	    }
3931 	  else
3932 	    {
3933 		spatialite_e
3934 		    ("step: SELECT FROM networks error: \"%s\"\n",
3935 		     sqlite3_errmsg (handle));
3936 		sqlite3_finalize (stmt);
3937 		return NULL;
3938 	    }
3939       }
3940     sqlite3_finalize (stmt);
3941 
3942     if (ok)
3943       {
3944 	  *spatial = xspatial;
3945 	  *srid = xsrid;
3946 	  *has_z = xhas_z;
3947 	  *allow_coincident = xallow_coincident;
3948 	  return xnetwork_name;
3949       }
3950 
3951     if (xnetwork_name != NULL)
3952 	free (xnetwork_name);
3953     return NULL;
3954 }
3955 
3956 SPATIALITE_PRIVATE void
fnctaux_TopoNet_Clone(const void * xcontext,int argc,const void * xargv)3957 fnctaux_TopoNet_Clone (const void *xcontext, int argc, const void *xargv)
3958 {
3959 /* SQL function:
3960 / TopoNet_Clone ( text db-prefix, text in-network-name, text out-network-name )
3961 /
3962 / returns: 1 on success
3963 / raises an exception on failure
3964 */
3965     int ret;
3966     const char *db_prefix = "MAIN";
3967     const char *in_network_name;
3968     const char *out_network_name;
3969     char *input_network_name = NULL;
3970     int spatial;
3971     int srid;
3972     int has_z;
3973     int allow_coincident;
3974     GaiaNetworkAccessorPtr accessor;
3975     sqlite3_context *context = (sqlite3_context *) xcontext;
3976     sqlite3_value **argv = (sqlite3_value **) xargv;
3977     sqlite3 *sqlite = sqlite3_context_db_handle (context);
3978     struct splite_internal_cache *cache = sqlite3_user_data (context);
3979     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
3980     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
3981 	;
3982     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
3983 	db_prefix = (const char *) sqlite3_value_text (argv[0]);
3984     else
3985 	goto invalid_arg;
3986     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
3987 	goto null_arg;
3988     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
3989 	in_network_name = (const char *) sqlite3_value_text (argv[1]);
3990     else
3991 	goto invalid_arg;
3992     if (sqlite3_value_type (argv[2]) == SQLITE_NULL)
3993 	goto null_arg;
3994     else if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
3995 	out_network_name = (const char *) sqlite3_value_text (argv[2]);
3996     else
3997 	goto invalid_arg;
3998 
3999 /* checking the origin Network */
4000     input_network_name =
4001 	gaiaGetAttachedNetwork (sqlite, db_prefix, in_network_name, &spatial,
4002 				&srid, &has_z, &allow_coincident);
4003     if (input_network_name == NULL)
4004 	goto no_net;
4005 
4006 /* attempting to create the destination Network */
4007     start_net_savepoint (sqlite, cache);
4008     ret =
4009 	gaiaNetworkCreate (sqlite, out_network_name, spatial, srid,
4010 			   has_z, allow_coincident);
4011     if (!ret)
4012       {
4013 	  rollback_net_savepoint (sqlite, cache);
4014 	  goto no_net2;
4015       }
4016 
4017 /* attempting to get a Network Accessor (destination) */
4018     accessor = gaiaGetNetwork (sqlite, cache, out_network_name);
4019     if (accessor == NULL)
4020       {
4021 	  rollback_net_savepoint (sqlite, cache);
4022 	  goto no_net2;
4023       }
4024 
4025 /* cloning Network */
4026     ret = do_clone_network (db_prefix, input_network_name, accessor);
4027     if (!ret)
4028 	rollback_net_savepoint (sqlite, cache);
4029     else
4030 	release_net_savepoint (sqlite, cache);
4031     if (!ret)
4032       {
4033 	  sqlite3_result_error (context, "Clone Network failure", -1);
4034 	  return;
4035       }
4036     sqlite3_result_int (context, 1);
4037     free (input_network_name);
4038     return;
4039 
4040   no_net:
4041     if (input_network_name != NULL)
4042 	free (input_network_name);
4043     sqlite3_result_error (context,
4044 			  "SQL/MM Spatial exception - invalid network name (origin).",
4045 			  -1);
4046     return;
4047 
4048   no_net2:
4049     if (input_network_name != NULL)
4050 	free (input_network_name);
4051     sqlite3_result_error (context,
4052 			  "SQL/MM Spatial exception - invalid network name (destination).",
4053 			  -1);
4054     return;
4055 
4056   null_arg:
4057     if (input_network_name != NULL)
4058 	free (input_network_name);
4059     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
4060 			  -1);
4061     return;
4062 
4063   invalid_arg:
4064     if (input_network_name != NULL)
4065 	free (input_network_name);
4066     sqlite3_result_error (context,
4067 			  "SQL/MM Spatial exception - invalid argument.", -1);
4068     return;
4069 }
4070 
4071 SPATIALITE_PRIVATE void
fnctaux_TopoNet_GetLinkSeed(const void * xcontext,int argc,const void * xargv)4072 fnctaux_TopoNet_GetLinkSeed (const void *xcontext, int argc, const void *xargv)
4073 {
4074 /* SQL function:
4075 / TopoNet_GetLinkSeed ( text network-name, int link_id )
4076 /
4077 / returns: a Point (seed) identifying the Link
4078 / raises an exception on failure
4079 */
4080     const char *network_name;
4081     sqlite3_int64 link_id;
4082     unsigned char *p_blob;
4083     int n_bytes;
4084     gaiaGeomCollPtr geom;
4085     GaiaNetworkAccessorPtr accessor;
4086     int gpkg_mode = 0;
4087     int tiny_point = 0;
4088     sqlite3_context *context = (sqlite3_context *) xcontext;
4089     sqlite3_value **argv = (sqlite3_value **) xargv;
4090     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4091     struct splite_internal_cache *cache = sqlite3_user_data (context);
4092     struct gaia_network *net;
4093     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4094     if (cache != NULL)
4095       {
4096 	  gpkg_mode = cache->gpkg_mode;
4097 	  tiny_point = cache->tinyPointEnabled;
4098       }
4099     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4100 	goto null_arg;
4101     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4102 	network_name = (const char *) sqlite3_value_text (argv[0]);
4103     else
4104 	goto invalid_arg;
4105     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4106 	goto null_arg;
4107     else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
4108 	link_id = sqlite3_value_int64 (argv[1]);
4109     else
4110 	goto invalid_arg;
4111 
4112 /* attempting to get a Network Accessor */
4113     accessor = gaiaGetNetwork (sqlite, cache, network_name);
4114     if (accessor == NULL)
4115 	goto no_net;
4116     net = (struct gaia_network *) accessor;
4117     if (net->spatial == 0)
4118 	goto logical_err;
4119 
4120     gaianet_reset_last_error_msg (accessor);
4121     geom = gaiaGetLinkSeed (accessor, link_id);
4122     if (geom == NULL)
4123       {
4124 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
4125 	  if (msg != NULL)
4126 	    {
4127 		gaianet_set_last_error_msg (accessor, msg);
4128 		sqlite3_result_error (context, msg, -1);
4129 		return;
4130 	    }
4131 	  sqlite3_result_null (context);
4132 	  return;
4133       }
4134     gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode, tiny_point);
4135     gaiaFreeGeomColl (geom);
4136     if (p_blob == NULL)
4137 	sqlite3_result_null (context);
4138     else
4139 	sqlite3_result_blob (context, p_blob, n_bytes, free);
4140     return;
4141 
4142   no_net:
4143     sqlite3_result_error (context,
4144 			  "SQL/MM Spatial exception - invalid network name.",
4145 			  -1);
4146     return;
4147 
4148   null_arg:
4149     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
4150 			  -1);
4151     return;
4152 
4153   invalid_arg:
4154     sqlite3_result_error (context,
4155 			  "SQL/MM Spatial exception - invalid argument.", -1);
4156     return;
4157 
4158   logical_err:
4159     sqlite3_result_error (context,
4160 			  "TopoNet_GetLinkSeed() cannot be applied to Logical Network.",
4161 			  -1);
4162     return;
4163 }
4164 
4165 SPATIALITE_PRIVATE void
fnctaux_TopoNet_UpdateSeeds(const void * xcontext,int argc,const void * xargv)4166 fnctaux_TopoNet_UpdateSeeds (const void *xcontext, int argc, const void *xargv)
4167 {
4168 /* SQL function:
4169 / TopoNet_UpdateSeeds ( text network-name )
4170 / TopoNet_UpdateSeeds ( text network-name, int incremental_mode )
4171 /
4172 / returns: 1 on success
4173 / raises an exception on failure
4174 */
4175     const char *network_name;
4176     int incremental_mode = 1;
4177     int ret;
4178     GaiaNetworkAccessorPtr accessor;
4179     sqlite3_context *context = (sqlite3_context *) xcontext;
4180     sqlite3_value **argv = (sqlite3_value **) xargv;
4181     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4182     struct splite_internal_cache *cache = sqlite3_user_data (context);
4183     struct gaia_network *net;
4184     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4185     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4186 	goto null_arg;
4187     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4188 	network_name = (const char *) sqlite3_value_text (argv[0]);
4189     else
4190 	goto invalid_arg;
4191     if (argc >= 2)
4192       {
4193 	  if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4194 	      goto null_arg;
4195 	  else if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
4196 	      incremental_mode = sqlite3_value_int (argv[1]);
4197 	  else
4198 	      goto invalid_arg;
4199       }
4200 
4201 /* attempting to get a Network Accessor */
4202     accessor = gaiaGetNetwork (sqlite, cache, network_name);
4203     if (accessor == NULL)
4204 	goto no_net;
4205     net = (struct gaia_network *) accessor;
4206     if (net->spatial == 0)
4207 	goto logical_err;
4208 
4209     gaianet_reset_last_error_msg (accessor);
4210     start_net_savepoint (sqlite, cache);
4211     ret = gaiaTopoNetUpdateSeeds (accessor, incremental_mode);
4212     if (!ret)
4213 	rollback_net_savepoint (sqlite, cache);
4214     else
4215 	release_net_savepoint (sqlite, cache);
4216     if (!ret)
4217       {
4218 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
4219 	  if (msg != NULL)
4220 	    {
4221 		gaianet_set_last_error_msg (accessor, msg);
4222 		sqlite3_result_error (context, msg, -1);
4223 		return;
4224 	    }
4225 	  sqlite3_result_null (context);
4226 	  return;
4227       }
4228     sqlite3_result_int (context, 1);
4229     return;
4230 
4231   no_net:
4232     sqlite3_result_error (context,
4233 			  "SQL/MM Spatial exception - invalid network name.",
4234 			  -1);
4235     return;
4236 
4237   null_arg:
4238     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
4239 			  -1);
4240     return;
4241 
4242   invalid_arg:
4243     sqlite3_result_error (context,
4244 			  "SQL/MM Spatial exception - invalid argument.", -1);
4245     return;
4246 
4247   logical_err:
4248     sqlite3_result_error (context,
4249 			  "TopoNet_UpdateSeeds() cannot be applied to Logical Network.",
4250 			  -1);
4251     return;
4252 }
4253 
4254 SPATIALITE_PRIVATE void
fnctaux_TopoNet_DisambiguateSegmentLinks(const void * xcontext,int argc,const void * xargv)4255 fnctaux_TopoNet_DisambiguateSegmentLinks (const void *xcontext, int argc,
4256 					  const void *xargv)
4257 {
4258 /* SQL function:
4259 / TopoNet_DisambiguateSegmentLinks ( text network-name )
4260 /
4261 / returns: the total number of changed Links.
4262 / raises an exception on failure
4263 */
4264     const char *network_name;
4265     int changed_links = 0;
4266     GaiaNetworkAccessorPtr accessor;
4267     sqlite3_context *context = (sqlite3_context *) xcontext;
4268     sqlite3_value **argv = (sqlite3_value **) xargv;
4269     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4270     struct splite_internal_cache *cache = sqlite3_user_data (context);
4271     struct gaia_network *net;
4272     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4273     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4274 	goto null_arg;
4275     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4276 	network_name = (const char *) sqlite3_value_text (argv[0]);
4277     else
4278 	goto invalid_arg;
4279 
4280 /* attempting to get a Network Accessor */
4281     accessor = gaiaGetNetwork (sqlite, cache, network_name);
4282     if (accessor == NULL)
4283 	goto no_net;
4284     net = (struct gaia_network *) accessor;
4285     if (net->spatial == 0)
4286 	goto logical_err;
4287 
4288     gaianet_reset_last_error_msg (accessor);
4289     start_net_savepoint (sqlite, cache);
4290     changed_links = gaiaTopoNet_DisambiguateSegmentLinks (accessor);
4291     if (changed_links < 0)
4292 	rollback_net_savepoint (sqlite, cache);
4293     else
4294 	release_net_savepoint (sqlite, cache);
4295     if (changed_links < 0)
4296       {
4297 	  const char *msg = lwn_GetErrorMsg (net->lwn_iface);
4298 	  if (msg != NULL)
4299 	    {
4300 		gaianet_set_last_error_msg (accessor, msg);
4301 		sqlite3_result_error (context, msg, -1);
4302 		return;
4303 	    }
4304 	  sqlite3_result_null (context);
4305 	  return;
4306       }
4307     sqlite3_result_int (context, changed_links);
4308     return;
4309 
4310   no_net:
4311     sqlite3_result_error (context,
4312 			  "SQL/MM Spatial exception - invalid network name.",
4313 			  -1);
4314     return;
4315 
4316   null_arg:
4317     sqlite3_result_error (context, "SQL/MM Spatial exception - null argument.",
4318 			  -1);
4319     return;
4320 
4321   invalid_arg:
4322     sqlite3_result_error (context,
4323 			  "SQL/MM Spatial exception - invalid argument.", -1);
4324     return;
4325 
4326   logical_err:
4327     sqlite3_result_error (context,
4328 			  "TopoNet_UpdateSeeds() cannot be applied to Logical Network.",
4329 			  -1);
4330     return;
4331 }
4332 
4333 static int
check_matching_srid(GaiaNetworkAccessorPtr accessor,int srid)4334 check_matching_srid (GaiaNetworkAccessorPtr accessor, int srid)
4335 {
4336 /* checking for matching SRID */
4337     struct gaia_network *network = (struct gaia_network *) accessor;
4338     if (network->srid != srid)
4339 	return 0;
4340     return 1;
4341 }
4342 
4343 SPATIALITE_PRIVATE void
fnctaux_TopoNet_LineLinksList(const void * xcontext,int argc,const void * xargv)4344 fnctaux_TopoNet_LineLinksList (const void *xcontext, int argc,
4345 			       const void *xargv)
4346 {
4347 /* SQL function:
4348 / TopoNet_LineLinksList ( text network-name, text db-prefix, text ref_table,
4349 /                         text ref_column, text out_table )
4350 /
4351 / returns: 1 on success
4352 / raises an exception on failure
4353 */
4354     const char *msg;
4355     int ret;
4356     const char *network_name;
4357     const char *db_prefix;
4358     const char *ref_table;
4359     const char *ref_column;
4360     const char *out_table;
4361     char *xreftable = NULL;
4362     char *xrefcolumn = NULL;
4363     int srid;
4364     int family;
4365     GaiaNetworkAccessorPtr accessor = NULL;
4366     sqlite3_context *context = (sqlite3_context *) xcontext;
4367     sqlite3_value **argv = (sqlite3_value **) xargv;
4368     sqlite3 *sqlite = sqlite3_context_db_handle (context);
4369     struct splite_internal_cache *cache = sqlite3_user_data (context);
4370     GAIA_UNUSED ();		/* LCOV_EXCL_LINE */
4371     if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
4372 	goto null_arg;
4373     else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
4374 	network_name = (const char *) sqlite3_value_text (argv[0]);
4375     else
4376 	goto invalid_arg;
4377     if (sqlite3_value_type (argv[1]) == SQLITE_NULL)
4378 	db_prefix = "main";
4379     else if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
4380 	db_prefix = (const char *) sqlite3_value_text (argv[1]);
4381     else
4382 	goto invalid_arg;
4383     if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
4384 	ref_table = (const char *) sqlite3_value_text (argv[2]);
4385     else
4386 	goto invalid_arg;
4387     if (sqlite3_value_type (argv[3]) == SQLITE_NULL)
4388 	ref_column = NULL;
4389     else if (sqlite3_value_type (argv[3]) == SQLITE_TEXT)
4390 	ref_column = (const char *) sqlite3_value_text (argv[3]);
4391     else
4392 	goto invalid_arg;
4393     if (sqlite3_value_type (argv[4]) == SQLITE_NULL)
4394 	goto null_arg;
4395     else if (sqlite3_value_type (argv[4]) == SQLITE_TEXT)
4396 	out_table = (const char *) sqlite3_value_text (argv[4]);
4397     else
4398 	goto invalid_arg;
4399 
4400 /* attempting to get a Network Accessor */
4401     accessor = gaiaGetNetwork (sqlite, cache, network_name);
4402     if (accessor == NULL)
4403 	goto no_net;
4404     gaianet_reset_last_error_msg (accessor);
4405 
4406 /* checking the reference GeoTable */
4407     if (!gaia_check_reference_geo_table
4408 	(sqlite, db_prefix, ref_table, ref_column, &xreftable, &xrefcolumn,
4409 	 &srid, &family))
4410 	goto no_reference;
4411     if (!check_matching_srid (accessor, srid))
4412 	goto invalid_geom;
4413     if (family != GAIA_TYPE_LINESTRING)
4414 	goto not_linestring;
4415 
4416 /* checking the output Table */
4417     if (!gaia_check_output_table (sqlite, out_table))
4418 	goto err_output;
4419 
4420     start_topo_savepoint (sqlite, cache);
4421     ret =
4422 	gaiaTopoNet_LineLinksList (accessor, db_prefix, xreftable, xrefcolumn,
4423 				   out_table);
4424     if (!ret)
4425 	rollback_topo_savepoint (sqlite, cache);
4426     else
4427 	release_topo_savepoint (sqlite, cache);
4428     free (xreftable);
4429     free (xrefcolumn);
4430     if (!ret)
4431       {
4432 	  msg = gaiaGetRtTopoErrorMsg (cache);
4433 	  gaianet_set_last_error_msg (accessor, msg);
4434 	  sqlite3_result_error (context, msg, -1);
4435 	  return;
4436       }
4437     sqlite3_result_int (context, 1);
4438     return;
4439 
4440   no_net:
4441     if (xreftable != NULL)
4442 	free (xreftable);
4443     if (xrefcolumn != NULL)
4444 	free (xrefcolumn);
4445     msg = "SQL/MM Spatial exception - invalid network name.";
4446     gaianet_set_last_error_msg (accessor, msg);
4447     sqlite3_result_error (context, msg, -1);
4448     return;
4449 
4450   no_reference:
4451     if (xreftable != NULL)
4452 	free (xreftable);
4453     if (xrefcolumn != NULL)
4454 	free (xrefcolumn);
4455     msg = "TopoGeo_LineLinksList: invalid reference GeoTable.";
4456     gaianet_set_last_error_msg (accessor, msg);
4457     sqlite3_result_error (context, msg, -1);
4458     return;
4459 
4460   err_output:
4461     if (xreftable != NULL)
4462 	free (xreftable);
4463     if (xrefcolumn != NULL)
4464 	free (xrefcolumn);
4465     msg = "TopoNet_LineLinksList: output GeoTable already exists.";
4466     gaianet_set_last_error_msg (accessor, msg);
4467     sqlite3_result_error (context, msg, -1);
4468     return;
4469 
4470   null_arg:
4471     if (xreftable != NULL)
4472 	free (xreftable);
4473     if (xrefcolumn != NULL)
4474 	free (xrefcolumn);
4475     msg = "SQL/MM Spatial exception - null argument.";
4476     gaianet_set_last_error_msg (accessor, msg);
4477     sqlite3_result_error (context, msg, -1);
4478     return;
4479 
4480   invalid_arg:
4481     if (xreftable != NULL)
4482 	free (xreftable);
4483     if (xrefcolumn != NULL)
4484 	free (xrefcolumn);
4485     msg = "SQL/MM Spatial exception - invalid argument.";
4486     gaianet_set_last_error_msg (accessor, msg);
4487     sqlite3_result_error (context, msg, -1);
4488     return;
4489 
4490   invalid_geom:
4491     if (xreftable != NULL)
4492 	free (xreftable);
4493     if (xrefcolumn != NULL)
4494 	free (xrefcolumn);
4495     msg =
4496 	"SQL/MM Spatial exception - invalid reference GeoTable (mismatching SRID).";
4497     gaianet_set_last_error_msg (accessor, msg);
4498     sqlite3_result_error (context, msg, -1);
4499     return;
4500 
4501   not_linestring:
4502     if (xreftable != NULL)
4503 	free (xreftable);
4504     if (xrefcolumn != NULL)
4505 	free (xrefcolumn);
4506     msg =
4507 	"SQL/MM Spatial exception - invalid reference GeoTable (not of the [MULTI]LINESTRING type).";
4508     gaianet_set_last_error_msg (accessor, msg);
4509     sqlite3_result_error (context, msg, -1);
4510     return;
4511 }
4512 
4513 #endif /* end RTTOPO conditionals */
4514