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