1 /**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * Copyright (C) 2015 Sandro Santilli <strk@kbt.io>
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU General Public Licence. See the COPYING file.
10 *
11 **********************************************************************/
12
13 #include "postgres.h"
14 #include "fmgr.h"
15 #include "c.h" /* for UINT64_FORMAT and uint64 */
16 #include "utils/builtins.h"
17 #include "utils/elog.h"
18 #include "utils/memutils.h" /* for TopMemoryContext */
19 #include "utils/array.h" /* for ArrayType */
20 #include "catalog/pg_type.h" /* for INT4OID */
21 #include "lib/stringinfo.h"
22 #include "access/xact.h" /* for RegisterXactCallback */
23 #include "funcapi.h" /* for FuncCallContext */
24 #include "executor/spi.h" /* this is what you need to work with SPI */
25 #include "inttypes.h" /* for PRId64 */
26
27 #include "../postgis_config.h"
28
29 #include "liblwgeom_internal.h" /* for gbox_clone */
30 #include "liblwgeom_topo.h"
31
32 /*#define POSTGIS_DEBUG_LEVEL 1*/
33 #include "lwgeom_log.h"
34 #include "lwgeom_pg.h"
35
36 #include <stdarg.h>
37
38 #ifndef __GNUC__
39 # define __attribute__ (x)
40 #endif
41
42 #define ABS(x) (x<0?-x:x)
43
44 #ifdef WIN32
45 # define LWTFMT_ELEMID "lld"
46 #else
47 # define LWTFMT_ELEMID PRId64
48 #endif
49
50 /*
51 * This is required for builds against pgsql
52 */
53 PG_MODULE_MAGIC;
54
55 LWT_BE_IFACE* be_iface;
56
57 /*
58 * Private data we'll use for this backend
59 */
60 #define MAXERRLEN 256
61 struct LWT_BE_DATA_T
62 {
63 char lastErrorMsg[MAXERRLEN];
64 /*
65 * This flag will need to be set to false
66 * at top-level function enter and set true
67 * whenever an callback changes the data
68 * in the database.
69 * It will be used by SPI_execute calls to
70 * make sure to see any data change occurring
71 * doring operations.
72 */
73 bool data_changed;
74
75 int topoLoadFailMessageFlavor; /* 0:sql, 1:AddPoint */
76 };
77
78 LWT_BE_DATA be_data;
79
80 struct LWT_BE_TOPOLOGY_T
81 {
82 LWT_BE_DATA* be_data;
83 char *name;
84 int id;
85 int srid;
86 double precision;
87 int hasZ;
88 Oid geometryOID;
89 };
90
91 /* utility funx */
92
93 static void cberror(const LWT_BE_DATA* be, const char *fmt, ...)
94 __attribute__ (( format(printf, 2, 3) ));
95
96 static void
cberror(const LWT_BE_DATA * be_in,const char * fmt,...)97 cberror(const LWT_BE_DATA* be_in, const char *fmt, ...)
98 {
99 LWT_BE_DATA *be = (LWT_BE_DATA*)be_in;/*const cast*/
100 va_list ap;
101
102 va_start(ap, fmt);
103
104 vsnprintf (be->lastErrorMsg, MAXERRLEN, fmt, ap);
105 be->lastErrorMsg[MAXERRLEN-1]='\0';
106
107 va_end(ap);
108 }
109
110 static void
_lwtype_upper_name(int type,char * buf,size_t buflen)111 _lwtype_upper_name(int type, char *buf, size_t buflen)
112 {
113 char *ptr;
114 snprintf(buf, buflen, "%s", lwtype_name(type));
115 buf[buflen-1] = '\0';
116 ptr = buf;
117 while (*ptr)
118 {
119 *ptr = toupper(*ptr);
120 ++ptr;
121 }
122 }
123
124 /* Return an lwalloc'ed geometrical representation of the box */
125 static LWGEOM *
_box2d_to_lwgeom(const GBOX * bbox,int srid)126 _box2d_to_lwgeom(const GBOX *bbox, int srid)
127 {
128 POINTARRAY *pa = ptarray_construct(0, 0, 2);
129 POINT4D p;
130 LWLINE *line;
131
132 p.x = bbox->xmin;
133 p.y = bbox->ymin;
134 ptarray_set_point4d(pa, 0, &p);
135 p.x = bbox->xmax;
136 p.y = bbox->ymax;
137 ptarray_set_point4d(pa, 1, &p);
138 line = lwline_construct(srid, NULL, pa);
139 return lwline_as_lwgeom(line);
140 }
141
142 /* Return lwalloc'ed hexwkb representation for a GBOX */
143 static char *
_box2d_to_hexwkb(const GBOX * bbox,int srid)144 _box2d_to_hexwkb(const GBOX *bbox, int srid)
145 {
146 char *hex;
147 size_t sz;
148 LWGEOM *geom = _box2d_to_lwgeom(bbox, srid);
149 hex = lwgeom_to_hexwkb(geom, WKT_EXTENDED, &sz);
150 lwgeom_free(geom);
151 assert(hex[sz-1] == '\0');
152 return hex;
153 }
154
155 /* Backend callbacks */
156
157 static const char*
cb_lastErrorMessage(const LWT_BE_DATA * be)158 cb_lastErrorMessage(const LWT_BE_DATA* be)
159 {
160 return be->lastErrorMsg;
161 }
162
163 static LWT_BE_TOPOLOGY*
cb_loadTopologyByName(const LWT_BE_DATA * be,const char * name)164 cb_loadTopologyByName(const LWT_BE_DATA* be, const char *name)
165 {
166 int spi_result;
167 const char *sql;
168 Datum dat;
169 bool isnull;
170 LWT_BE_TOPOLOGY *topo;
171 MemoryContext oldcontext = CurrentMemoryContext;
172 Datum values[1];
173 Oid argtypes[1];
174 static SPIPlanPtr plan = NULL;
175
176 argtypes[0] = CSTRINGOID;
177 sql =
178 "SELECT id,srid,precision,null::geometry "
179 "FROM topology.topology WHERE name = $1::varchar";
180 if ( ! plan ) /* prepare on first call */
181 {
182 plan = SPI_prepare(sql, 1, argtypes);
183 if ( ! plan )
184 {
185 cberror(be, "unexpected return (%d) from query preparation: %s",
186 SPI_result, sql);
187 return NULL;
188 }
189 SPI_keepplan(plan);
190 /* SPI_freeplan to free, eventually */
191 }
192
193 /* execute */
194 values[0] = CStringGetDatum(name);
195 spi_result = SPI_execute_plan(plan, values, NULL, !be->data_changed, 1);
196 MemoryContextSwitchTo( oldcontext ); /* switch back */
197 if ( spi_result != SPI_OK_SELECT )
198 {
199 cberror(be, "unexpected return (%d) from query execution: %s", spi_result, sql);
200 return NULL;
201 }
202 if ( ! SPI_processed )
203 {
204 if ( be->topoLoadFailMessageFlavor == 1 )
205 {
206 cberror(be, "No topology with name \"%s\" in topology.topology", name);
207 }
208 else
209 {
210 cberror(be, "SQL/MM Spatial exception - invalid topology name");
211 }
212 return NULL;
213 }
214 if ( SPI_processed > 1 )
215 {
216 cberror(be, "multiple topologies named '%s' were found", name);
217 return NULL;
218 }
219
220 topo = palloc(sizeof(LWT_BE_TOPOLOGY));
221 topo->be_data = (LWT_BE_DATA *)be; /* const cast.. */
222 topo->name = pstrdup(name);
223
224 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
225 if ( isnull )
226 {
227 cberror(be, "Topology '%s' has null identifier", name);
228 SPI_freetuptable(SPI_tuptable);
229 return NULL;
230 }
231 topo->id = DatumGetInt32(dat);
232
233 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &isnull);
234 if ( isnull )
235 {
236 cberror(be, "Topology '%s' has null SRID", name);
237 SPI_freetuptable(SPI_tuptable);
238 return NULL;
239 }
240 topo->srid = DatumGetInt32(dat);
241 if ( topo->srid < 0 )
242 {
243 lwnotice("Topology SRID value %d converted to "
244 "the officially unknown SRID value %d", topo->srid, SRID_UNKNOWN);
245 topo->srid = SRID_UNKNOWN;
246 }
247
248 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &isnull);
249 if ( isnull )
250 {
251 lwnotice("Topology '%s' has null precision, taking as 0", name);
252 topo->precision = 0; /* TODO: should this be -1 instead ? */
253 }
254 else
255 {
256 topo->precision = DatumGetFloat8(dat);
257 }
258
259 /* we're dynamically querying geometry type here */
260 #if POSTGIS_PGSQL_VERSION < 110
261 topo->geometryOID = SPI_tuptable->tupdesc->attrs[3]->atttypid;
262 #else
263 topo->geometryOID = SPI_tuptable->tupdesc->attrs[3].atttypid;
264 #endif
265
266 POSTGIS_DEBUGF(1, "cb_loadTopologyByName: topo '%s' has "
267 "id %d, srid %d, precision %g",
268 name, topo->id, topo->srid, topo->precision);
269
270 SPI_freetuptable(SPI_tuptable);
271
272 return topo;
273 }
274
275 static int
cb_topoGetSRID(const LWT_BE_TOPOLOGY * topo)276 cb_topoGetSRID(const LWT_BE_TOPOLOGY* topo)
277 {
278 return topo->srid;
279 }
280
281 static int
cb_topoHasZ(const LWT_BE_TOPOLOGY * topo)282 cb_topoHasZ(const LWT_BE_TOPOLOGY* topo)
283 {
284 return topo->hasZ;
285 }
286
287 static double
cb_topoGetPrecision(const LWT_BE_TOPOLOGY * topo)288 cb_topoGetPrecision(const LWT_BE_TOPOLOGY* topo)
289 {
290 return topo->precision;
291 }
292
293 static int
cb_freeTopology(LWT_BE_TOPOLOGY * topo)294 cb_freeTopology(LWT_BE_TOPOLOGY* topo)
295 {
296 pfree(topo->name);
297 pfree(topo);
298 return 1;
299 }
300
301 static void
addEdgeFields(StringInfo str,int fields,int fullEdgeData)302 addEdgeFields(StringInfo str, int fields, int fullEdgeData)
303 {
304 const char *sep = "";
305
306 if ( fields & LWT_COL_EDGE_EDGE_ID )
307 {
308 appendStringInfoString(str, "edge_id");
309 sep = ",";
310 }
311 if ( fields & LWT_COL_EDGE_START_NODE )
312 {
313 appendStringInfo(str, "%sstart_node", sep);
314 sep = ",";
315 }
316 if ( fields & LWT_COL_EDGE_END_NODE )
317 {
318 appendStringInfo(str, "%send_node", sep);
319 sep = ",";
320 }
321 if ( fields & LWT_COL_EDGE_FACE_LEFT )
322 {
323 appendStringInfo(str, "%sleft_face", sep);
324 sep = ",";
325 }
326 if ( fields & LWT_COL_EDGE_FACE_RIGHT )
327 {
328 appendStringInfo(str, "%sright_face", sep);
329 sep = ",";
330 }
331 if ( fields & LWT_COL_EDGE_NEXT_LEFT )
332 {
333 appendStringInfo(str, "%snext_left_edge", sep);
334 if ( fullEdgeData ) appendStringInfoString(str, ", abs_next_left_edge");
335 sep = ",";
336 }
337 if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
338 {
339 appendStringInfo(str, "%snext_right_edge", sep);
340 if ( fullEdgeData ) appendStringInfoString(str, ", abs_next_right_edge");
341 sep = ",";
342 }
343 if ( fields & LWT_COL_EDGE_GEOM )
344 {
345 appendStringInfo(str, "%sgeom", sep);
346 }
347 }
348
349 /* Add edge values in text form, include the parens */
350 static void
addEdgeValues(StringInfo str,const LWT_ISO_EDGE * edge,int fields,int fullEdgeData)351 addEdgeValues(StringInfo str, const LWT_ISO_EDGE *edge, int fields, int fullEdgeData)
352 {
353 size_t hexewkb_size;
354 char *hexewkb;
355 const char *sep = "";
356
357 appendStringInfoChar(str, '(');
358 if ( fields & LWT_COL_EDGE_EDGE_ID )
359 {
360 if ( edge->edge_id != -1 )
361 appendStringInfo(str, "%" LWTFMT_ELEMID, edge->edge_id);
362 else
363 appendStringInfoString(str, "DEFAULT");
364 sep = ",";
365 }
366 if ( fields & LWT_COL_EDGE_START_NODE )
367 {
368 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->start_node);
369 sep = ",";
370 }
371 if ( fields & LWT_COL_EDGE_END_NODE )
372 {
373 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->end_node);
374 sep = ",";
375 }
376 if ( fields & LWT_COL_EDGE_FACE_LEFT )
377 {
378 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->face_left);
379 sep = ",";
380 }
381 if ( fields & LWT_COL_EDGE_FACE_RIGHT )
382 {
383 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->face_right);
384 sep = ",";
385 }
386 if ( fields & LWT_COL_EDGE_NEXT_LEFT )
387 {
388 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->next_left);
389 if ( fullEdgeData )
390 appendStringInfo(str, ",%" LWTFMT_ELEMID, ABS(edge->next_left));
391 sep = ",";
392 }
393 if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
394 {
395 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, edge->next_right);
396 if ( fullEdgeData )
397 appendStringInfo(str, ",%" LWTFMT_ELEMID, ABS(edge->next_right));
398 sep = ",";
399 }
400 if ( fields & LWT_COL_EDGE_GEOM )
401 {
402 if ( edge->geom )
403 {
404 hexewkb = lwgeom_to_hexwkb(lwline_as_lwgeom(edge->geom),
405 WKB_EXTENDED, &hexewkb_size);
406 appendStringInfo(str, "%s'%s'::geometry", sep, hexewkb);
407 lwfree(hexewkb);
408 }
409 else
410 {
411 appendStringInfo(str, "%snull", sep);
412 }
413 }
414 appendStringInfoChar(str, ')');
415 }
416
417 enum UpdateType
418 {
419 updSet,
420 updSel,
421 updNot
422 };
423
424 static void
addEdgeUpdate(StringInfo str,const LWT_ISO_EDGE * edge,int fields,int fullEdgeData,enum UpdateType updType)425 addEdgeUpdate(StringInfo str, const LWT_ISO_EDGE* edge, int fields,
426 int fullEdgeData, enum UpdateType updType)
427 {
428 const char *sep = "";
429 const char *sep1;
430 const char *op;
431 size_t hexewkb_size;
432 char *hexewkb;
433
434 switch (updType)
435 {
436 case updSet:
437 op = "=";
438 sep1 = ",";
439 break;
440 case updSel:
441 op = "=";
442 sep1 = " AND ";
443 break;
444 case updNot:
445 default:
446 op = "!=";
447 sep1 = " AND ";
448 break;
449 }
450
451 if ( fields & LWT_COL_EDGE_EDGE_ID )
452 {
453 appendStringInfoString(str, "edge_id ");
454 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->edge_id);
455 sep = sep1;
456 }
457 if ( fields & LWT_COL_EDGE_START_NODE )
458 {
459 appendStringInfo(str, "%sstart_node ", sep);
460 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->start_node);
461 sep = sep1;
462 }
463 if ( fields & LWT_COL_EDGE_END_NODE )
464 {
465 appendStringInfo(str, "%send_node", sep);
466 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->end_node);
467 sep = sep1;
468 }
469 if ( fields & LWT_COL_EDGE_FACE_LEFT )
470 {
471 appendStringInfo(str, "%sleft_face", sep);
472 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->face_left);
473 sep = sep1;
474 }
475 if ( fields & LWT_COL_EDGE_FACE_RIGHT )
476 {
477 appendStringInfo(str, "%sright_face", sep);
478 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->face_right);
479 sep = sep1;
480 }
481 if ( fields & LWT_COL_EDGE_NEXT_LEFT )
482 {
483 appendStringInfo(str, "%snext_left_edge", sep);
484 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->next_left);
485 sep = sep1;
486 if ( fullEdgeData )
487 {
488 appendStringInfo(str, "%s abs_next_left_edge", sep);
489 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, ABS(edge->next_left));
490 }
491 }
492 if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
493 {
494 appendStringInfo(str, "%snext_right_edge", sep);
495 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, edge->next_right);
496 sep = sep1;
497 if ( fullEdgeData )
498 {
499 appendStringInfo(str, "%s abs_next_right_edge", sep);
500 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, ABS(edge->next_right));
501 }
502 }
503 if ( fields & LWT_COL_EDGE_GEOM )
504 {
505 appendStringInfo(str, "%sgeom", sep);
506 hexewkb = lwgeom_to_hexwkb(lwline_as_lwgeom(edge->geom),
507 WKB_EXTENDED, &hexewkb_size);
508 appendStringInfo(str, "%s'%s'::geometry", op, hexewkb);
509 lwfree(hexewkb);
510 }
511 }
512
513 static void
addNodeUpdate(StringInfo str,const LWT_ISO_NODE * node,int fields,int fullNodeData,enum UpdateType updType)514 addNodeUpdate(StringInfo str, const LWT_ISO_NODE* node, int fields,
515 int fullNodeData, enum UpdateType updType)
516 {
517 const char *sep = "";
518 const char *sep1;
519 const char *op;
520 size_t hexewkb_size;
521 char *hexewkb;
522
523 switch (updType)
524 {
525 case updSet:
526 op = "=";
527 sep1 = ",";
528 break;
529 case updSel:
530 op = "=";
531 sep1 = " AND ";
532 break;
533 case updNot:
534 default:
535 op = "!=";
536 sep1 = " AND ";
537 break;
538 }
539
540 if ( fields & LWT_COL_NODE_NODE_ID )
541 {
542 appendStringInfoString(str, "node_id ");
543 appendStringInfo(str, "%s %" LWTFMT_ELEMID, op, node->node_id);
544 sep = sep1;
545 }
546 if ( fields & LWT_COL_NODE_CONTAINING_FACE )
547 {
548 appendStringInfo(str, "%scontaining_face %s", sep, op);
549 if ( node->containing_face != -1 )
550 {
551 appendStringInfo(str, "%" LWTFMT_ELEMID, node->containing_face);
552 }
553 else
554 {
555 appendStringInfoString(str, "null::int");
556 }
557 sep = sep1;
558 }
559 if ( fields & LWT_COL_NODE_GEOM )
560 {
561 appendStringInfo(str, "%sgeom", sep);
562 hexewkb = lwgeom_to_hexwkb(lwpoint_as_lwgeom(node->geom),
563 WKB_EXTENDED, &hexewkb_size);
564 appendStringInfo(str, "%s'%s'::geometry", op, hexewkb);
565 lwfree(hexewkb);
566 }
567 }
568
569 static void
addNodeFields(StringInfo str,int fields)570 addNodeFields(StringInfo str, int fields)
571 {
572 const char *sep = "";
573
574 if ( fields & LWT_COL_NODE_NODE_ID )
575 {
576 appendStringInfoString(str, "node_id");
577 sep = ",";
578 }
579 if ( fields & LWT_COL_NODE_CONTAINING_FACE )
580 {
581 appendStringInfo(str, "%scontaining_face", sep);
582 sep = ",";
583 }
584 if ( fields & LWT_COL_NODE_GEOM )
585 {
586 appendStringInfo(str, "%sgeom", sep);
587 }
588 }
589
590 static void
addFaceFields(StringInfo str,int fields)591 addFaceFields(StringInfo str, int fields)
592 {
593 const char *sep = "";
594
595 if ( fields & LWT_COL_FACE_FACE_ID )
596 {
597 appendStringInfoString(str, "face_id");
598 sep = ",";
599 }
600 if ( fields & LWT_COL_FACE_MBR )
601 {
602 appendStringInfo(str, "%smbr", sep);
603 sep = ",";
604 }
605 }
606
607 /* Add node values for an insert, in text form */
608 static void
addNodeValues(StringInfo str,const LWT_ISO_NODE * node,int fields)609 addNodeValues(StringInfo str, const LWT_ISO_NODE *node, int fields)
610 {
611 size_t hexewkb_size;
612 char *hexewkb;
613 const char *sep = "";
614
615 appendStringInfoChar(str, '(');
616
617 if ( fields & LWT_COL_NODE_NODE_ID )
618 {
619 if ( node->node_id != -1 )
620 appendStringInfo(str, "%" LWTFMT_ELEMID, node->node_id);
621 else
622 appendStringInfoString(str, "DEFAULT");
623 sep = ",";
624 }
625
626 if ( fields & LWT_COL_NODE_CONTAINING_FACE )
627 {
628 if ( node->containing_face != -1 )
629 appendStringInfo(str, "%s%" LWTFMT_ELEMID, sep, node->containing_face);
630 else appendStringInfo(str, "%snull::int", sep);
631 }
632
633 if ( fields & LWT_COL_NODE_GEOM )
634 {
635 if ( node->geom )
636 {
637 hexewkb = lwgeom_to_hexwkb(lwpoint_as_lwgeom(node->geom),
638 WKB_EXTENDED, &hexewkb_size);
639 appendStringInfo(str, "%s'%s'::geometry", sep, hexewkb);
640 lwfree(hexewkb);
641 }
642 else
643 {
644 appendStringInfo(str, "%snull::geometry", sep);
645 }
646 }
647
648 appendStringInfoChar(str, ')');
649 }
650
651 /* Add face values for an insert, in text form */
652 static void
addFaceValues(StringInfo str,LWT_ISO_FACE * face,int srid)653 addFaceValues(StringInfo str, LWT_ISO_FACE *face, int srid)
654 {
655 if ( face->face_id != -1 )
656 appendStringInfo(str, "(%" LWTFMT_ELEMID, face->face_id);
657 else
658 appendStringInfoString(str, "(DEFAULT");
659
660 if ( face->mbr )
661 {
662 {
663 char *hexbox;
664 hexbox = _box2d_to_hexwkb(face->mbr, srid);
665 appendStringInfo(str, ",ST_Envelope('%s'::geometry))", hexbox);
666 lwfree(hexbox);
667 }
668 }
669 else
670 {
671 appendStringInfoString(str, ",null::geometry)");
672 }
673 }
674
675 static void
fillEdgeFields(LWT_ISO_EDGE * edge,HeapTuple row,TupleDesc rowdesc,int fields)676 fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
677 {
678 bool isnull;
679 Datum dat;
680 int val;
681 GSERIALIZED *geom;
682 LWGEOM *lwg;
683 int colno = 0;
684
685 POSTGIS_DEBUGF(2, "fillEdgeFields: got %d atts and fields %x",
686 rowdesc->natts, fields);
687
688 if ( fields & LWT_COL_EDGE_EDGE_ID )
689 {
690 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
691 if ( isnull )
692 {
693 lwpgwarning("Found edge with NULL edge_id");
694 edge->edge_id = -1;
695 }
696 else
697 {
698 val = DatumGetInt32(dat);
699 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (edge_id)"
700 " has int32 val of %d",
701 colno, val);
702 edge->edge_id = val;
703 }
704
705 }
706 if ( fields & LWT_COL_EDGE_START_NODE )
707 {
708 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
709 if ( isnull )
710 {
711 lwpgwarning("Found edge with NULL start_node");
712 edge->start_node = -1;
713 }
714 else
715 {
716 val = DatumGetInt32(dat);
717 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (start_node)"
718 " has int32 val of %d", colno, val);
719 edge->start_node = val;
720 }
721 }
722 if ( fields & LWT_COL_EDGE_END_NODE )
723 {
724 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
725 if ( isnull )
726 {
727 lwpgwarning("Found edge with NULL end_node");
728 edge->end_node = -1;
729 }
730 else
731 {
732 val = DatumGetInt32(dat);
733 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (end_node)"
734 " has int32 val of %d", colno, val);
735 edge->end_node = val;
736 }
737 }
738 if ( fields & LWT_COL_EDGE_FACE_LEFT )
739 {
740 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
741 if ( isnull )
742 {
743 lwpgwarning("Found edge with NULL face_left");
744 edge->face_left = -1;
745 }
746 else
747 {
748 val = DatumGetInt32(dat);
749 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (face_left)"
750 " has int32 val of %d", colno, val);
751 edge->face_left = val;
752 }
753 }
754 if ( fields & LWT_COL_EDGE_FACE_RIGHT )
755 {
756 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
757 if ( isnull )
758 {
759 lwpgwarning("Found edge with NULL face_right");
760 edge->face_right = -1;
761 }
762 else
763 {
764 val = DatumGetInt32(dat);
765 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (face_right)"
766 " has int32 val of %d", colno, val);
767 edge->face_right = val;
768 }
769 }
770 if ( fields & LWT_COL_EDGE_NEXT_LEFT )
771 {
772 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
773 if ( isnull )
774 {
775 lwpgwarning("Found edge with NULL next_left");
776 edge->next_left = -1;
777 }
778 else
779 {
780 val = DatumGetInt32(dat);
781 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (next_left)"
782 " has int32 val of %d", colno, val);
783 edge->next_left = val;
784 }
785 }
786 if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
787 {
788 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
789 if ( isnull )
790 {
791 lwpgwarning("Found edge with NULL next_right");
792 edge->next_right = -1;
793 }
794 else
795 {
796 val = DatumGetInt32(dat);
797 POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (next_right)"
798 " has int32 val of %d", colno, val);
799 edge->next_right = val;
800 }
801 }
802 if ( fields & LWT_COL_EDGE_GEOM )
803 {
804 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
805 if ( ! isnull )
806 {
807 {
808 MemoryContext oldcontext = CurrentMemoryContext;
809 geom = (GSERIALIZED *)PG_DETOAST_DATUM(dat);
810 lwg = lwgeom_from_gserialized(geom);
811 MemoryContextSwitchTo( TopMemoryContext );
812 edge->geom = lwgeom_as_lwline(lwgeom_clone_deep(lwg));
813 MemoryContextSwitchTo( oldcontext ); /* switch back */
814 lwgeom_free(lwg);
815 if ( DatumGetPointer(dat) != (Pointer)geom ) pfree(geom); /* IF_COPY */
816 }
817 }
818 else
819 {
820 lwpgwarning("Found edge with NULL geometry !");
821 edge->geom = NULL;
822 }
823 }
824 }
825
826 static void
fillNodeFields(LWT_ISO_NODE * node,HeapTuple row,TupleDesc rowdesc,int fields)827 fillNodeFields(LWT_ISO_NODE* node, HeapTuple row, TupleDesc rowdesc, int fields)
828 {
829 bool isnull;
830 Datum dat;
831 GSERIALIZED *geom;
832 LWGEOM *lwg;
833 int colno = 0;
834
835 if ( fields & LWT_COL_NODE_NODE_ID )
836 {
837 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
838 node->node_id = DatumGetInt32(dat);
839 }
840 if ( fields & LWT_COL_NODE_CONTAINING_FACE )
841 {
842 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
843 if ( isnull ) node->containing_face = -1;
844 else node->containing_face = DatumGetInt32(dat);
845 }
846 if ( fields & LWT_COL_NODE_GEOM )
847 {
848 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
849 if ( ! isnull )
850 {
851 geom = (GSERIALIZED *)PG_DETOAST_DATUM(dat);
852 lwg = lwgeom_from_gserialized(geom);
853 node->geom = lwgeom_as_lwpoint(lwgeom_clone_deep(lwg));
854 lwgeom_free(lwg);
855 if ( DatumGetPointer(dat) != (Pointer)geom ) pfree(geom); /* IF_COPY */
856 }
857 else
858 {
859 lwpgnotice("Found node with NULL geometry !");
860 node->geom = NULL;
861 }
862 }
863 }
864
865 static void
fillFaceFields(LWT_ISO_FACE * face,HeapTuple row,TupleDesc rowdesc,int fields)866 fillFaceFields(LWT_ISO_FACE* face, HeapTuple row, TupleDesc rowdesc, int fields)
867 {
868 bool isnull;
869 Datum dat;
870 GSERIALIZED *geom;
871 LWGEOM *g;
872 const GBOX *box;
873 int colno = 0;
874
875 if ( fields & LWT_COL_FACE_FACE_ID )
876 {
877 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
878 face->face_id = DatumGetInt32(dat);
879 }
880 if ( fields & LWT_COL_FACE_MBR )
881 {
882 dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
883 if ( ! isnull )
884 {
885 /* NOTE: this is a geometry of which we want to take (and clone) the BBOX */
886 geom = (GSERIALIZED *)PG_DETOAST_DATUM(dat);
887 g = lwgeom_from_gserialized(geom);
888 box = lwgeom_get_bbox(g);
889 if ( box )
890 {
891 face->mbr = gbox_clone(box);
892 }
893 else
894 {
895 lwpgnotice("Found face with EMPTY MBR !");
896 face->mbr = NULL;
897 }
898 lwgeom_free(g);
899 if ( DatumGetPointer(dat) != (Pointer)geom ) pfree(geom);
900 }
901 else
902 {
903 /* NOTE: perfectly fine for universe face */
904 POSTGIS_DEBUG(1, "Found face with NULL MBR");
905 face->mbr = NULL;
906 }
907 }
908 }
909
910 /* return 0 on failure (null) 1 otherwise */
911 static int
getNotNullInt32(HeapTuple row,TupleDesc desc,int col,int32 * val)912 getNotNullInt32( HeapTuple row, TupleDesc desc, int col, int32 *val )
913 {
914 bool isnull;
915 Datum dat = SPI_getbinval( row, desc, col, &isnull );
916 if ( isnull ) return 0;
917 *val = DatumGetInt32(dat);
918 return 1;
919 }
920
921 /* ----------------- Callbacks start here ------------------------ */
922
923 static LWT_ISO_EDGE*
cb_getEdgeById(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields)924 cb_getEdgeById(const LWT_BE_TOPOLOGY* topo,
925 const LWT_ELEMID* ids, int* numelems, int fields)
926 {
927 LWT_ISO_EDGE *edges;
928 int spi_result;
929 MemoryContext oldcontext = CurrentMemoryContext;
930 StringInfoData sqldata;
931 StringInfo sql = &sqldata;
932 int i;
933
934 initStringInfo(sql);
935 appendStringInfoString(sql, "SELECT ");
936 addEdgeFields(sql, fields, 0);
937 appendStringInfo(sql, " FROM \"%s\".edge_data", topo->name);
938 appendStringInfoString(sql, " WHERE edge_id IN (");
939 // add all identifiers here
940 for (i=0; i<*numelems; ++i)
941 {
942 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
943 }
944 appendStringInfoString(sql, ")");
945 POSTGIS_DEBUGF(1, "cb_getEdgeById query: %s", sql->data);
946
947 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems);
948 MemoryContextSwitchTo( oldcontext ); /* switch back */
949 if ( spi_result != SPI_OK_SELECT )
950 {
951 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
952 pfree(sqldata.data);
953 *numelems = -1;
954 return NULL;
955 }
956 pfree(sqldata.data);
957
958 POSTGIS_DEBUGF(1, "cb_getEdgeById: edge query returned %d rows", SPI_processed);
959 *numelems = SPI_processed;
960 if ( ! SPI_processed )
961 {
962 return NULL;
963 }
964
965 edges = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
966 for ( i=0; i<*numelems; ++i )
967 {
968 HeapTuple row = SPI_tuptable->vals[i];
969 fillEdgeFields(&edges[i], row, SPI_tuptable->tupdesc, fields);
970 }
971
972 SPI_freetuptable(SPI_tuptable);
973
974 return edges;
975 }
976
977 static LWT_ISO_EDGE*
cb_getEdgeByNode(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields)978 cb_getEdgeByNode(const LWT_BE_TOPOLOGY* topo,
979 const LWT_ELEMID* ids, int* numelems, int fields)
980 {
981 LWT_ISO_EDGE *edges;
982 int spi_result;
983
984 StringInfoData sqldata;
985 StringInfo sql = &sqldata;
986 int i;
987 MemoryContext oldcontext = CurrentMemoryContext;
988
989 initStringInfo(sql);
990 appendStringInfoString(sql, "SELECT ");
991 addEdgeFields(sql, fields, 0);
992 appendStringInfo(sql, " FROM \"%s\".edge_data", topo->name);
993 appendStringInfoString(sql, " WHERE start_node IN (");
994 // add all identifiers here
995 for (i=0; i<*numelems; ++i)
996 {
997 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
998 }
999 appendStringInfoString(sql, ") OR end_node IN (");
1000 // add all identifiers here
1001 for (i=0; i<*numelems; ++i)
1002 {
1003 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
1004 }
1005 appendStringInfoString(sql, ")");
1006
1007 POSTGIS_DEBUGF(1, "cb_getEdgeByNode query: %s", sql->data);
1008 POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
1009
1010 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
1011 MemoryContextSwitchTo( oldcontext ); /* switch back */
1012 if ( spi_result != SPI_OK_SELECT )
1013 {
1014 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1015 pfree(sqldata.data);
1016 *numelems = -1;
1017 return NULL;
1018 }
1019 pfree(sqldata.data);
1020
1021 POSTGIS_DEBUGF(1, "cb_getEdgeByNode: edge query returned %d rows", SPI_processed);
1022 *numelems = SPI_processed;
1023 if ( ! SPI_processed )
1024 {
1025 return NULL;
1026 }
1027
1028 edges = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
1029 for ( i=0; i<*numelems; ++i )
1030 {
1031 HeapTuple row = SPI_tuptable->vals[i];
1032 fillEdgeFields(&edges[i], row, SPI_tuptable->tupdesc, fields);
1033 }
1034
1035 SPI_freetuptable(SPI_tuptable);
1036
1037 return edges;
1038 }
1039
1040 static LWT_ISO_EDGE*
cb_getEdgeByFace(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields,const GBOX * box)1041 cb_getEdgeByFace(const LWT_BE_TOPOLOGY* topo,
1042 const LWT_ELEMID* ids, int* numelems, int fields,
1043 const GBOX *box)
1044 {
1045 LWT_ISO_EDGE *edges;
1046 int spi_result;
1047 MemoryContext oldcontext = CurrentMemoryContext;
1048 StringInfoData sqldata;
1049 StringInfo sql = &sqldata;
1050 int i;
1051 ArrayType *array_ids;
1052 Datum *datum_ids;
1053 Datum values[2];
1054 Oid argtypes[2];
1055 int nargs = 1;
1056 GSERIALIZED *gser = NULL;
1057
1058 datum_ids = palloc(sizeof(Datum)*(*numelems));
1059 for (i=0; i<*numelems; ++i) datum_ids[i] = Int32GetDatum(ids[i]);
1060 array_ids = construct_array(datum_ids, *numelems, INT4OID, 4, true, 's');
1061
1062 initStringInfo(sql);
1063 appendStringInfoString(sql, "SELECT ");
1064 addEdgeFields(sql, fields, 0);
1065 appendStringInfo(sql, " FROM \"%s\".edge_data"
1066 " WHERE ( left_face = ANY($1) "
1067 " OR right_face = ANY ($1) )",
1068 topo->name);
1069
1070 values[0] = PointerGetDatum(array_ids);
1071 argtypes[0] = INT4ARRAYOID;
1072
1073 if ( box )
1074 {
1075 LWGEOM *g = _box2d_to_lwgeom(box, topo->srid);
1076 gser = geometry_serialize(g);
1077 lwgeom_free(g);
1078 appendStringInfo(sql, " AND geom && $2");
1079
1080 values[1] = PointerGetDatum(gser);
1081 argtypes[1] = topo->geometryOID;
1082 ++nargs;
1083 }
1084
1085 POSTGIS_DEBUGF(1, "cb_getEdgeByFace query: %s", sql->data);
1086 POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
1087
1088 spi_result = SPI_execute_with_args(sql->data, nargs, argtypes, values, NULL,
1089 !topo->be_data->data_changed, 0);
1090 pfree(array_ids); /* not needed anymore */
1091 if ( gser ) pfree(gser); /* not needed anymore */
1092 MemoryContextSwitchTo( oldcontext ); /* switch back */
1093 if ( spi_result != SPI_OK_SELECT )
1094 {
1095 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1096 pfree(sqldata.data);
1097 *numelems = -1;
1098 return NULL;
1099 }
1100 pfree(sqldata.data);
1101
1102 POSTGIS_DEBUGF(1, "cb_getEdgeByFace: edge query returned %d rows", SPI_processed);
1103 *numelems = SPI_processed;
1104 if ( ! SPI_processed )
1105 {
1106 return NULL;
1107 }
1108
1109 edges = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
1110 for ( i=0; i<*numelems; ++i )
1111 {
1112 HeapTuple row = SPI_tuptable->vals[i];
1113 fillEdgeFields(&edges[i], row, SPI_tuptable->tupdesc, fields);
1114 }
1115
1116 SPI_freetuptable(SPI_tuptable);
1117
1118 return edges;
1119 }
1120
1121 static LWT_ISO_FACE*
cb_getFacesById(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields)1122 cb_getFacesById(const LWT_BE_TOPOLOGY* topo,
1123 const LWT_ELEMID* ids, int* numelems, int fields)
1124 {
1125 LWT_ISO_FACE *faces;
1126 int spi_result;
1127 StringInfoData sqldata;
1128 StringInfo sql = &sqldata;
1129 int i;
1130 MemoryContext oldcontext = CurrentMemoryContext;
1131
1132 initStringInfo(sql);
1133 appendStringInfoString(sql, "SELECT ");
1134 addFaceFields(sql, fields);
1135 appendStringInfo(sql, " FROM \"%s\".face", topo->name);
1136 appendStringInfoString(sql, " WHERE face_id IN (");
1137 // add all identifiers here
1138 for (i=0; i<*numelems; ++i)
1139 {
1140 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
1141 }
1142 appendStringInfoString(sql, ")");
1143
1144 POSTGIS_DEBUGF(1, "cb_getFaceById query: %s", sql->data);
1145 POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
1146
1147 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
1148 MemoryContextSwitchTo( oldcontext ); /* switch back */
1149 if ( spi_result != SPI_OK_SELECT )
1150 {
1151 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1152 pfree(sqldata.data);
1153 *numelems = -1;
1154 return NULL;
1155 }
1156 pfree(sqldata.data);
1157
1158 POSTGIS_DEBUGF(1, "cb_getFaceById: face query returned %d rows", SPI_processed);
1159 *numelems = SPI_processed;
1160 if ( ! SPI_processed )
1161 {
1162 return NULL;
1163 }
1164
1165 faces = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
1166 for ( i=0; i<*numelems; ++i )
1167 {
1168 HeapTuple row = SPI_tuptable->vals[i];
1169 fillFaceFields(&faces[i], row, SPI_tuptable->tupdesc, fields);
1170 }
1171
1172 SPI_freetuptable(SPI_tuptable);
1173
1174 return faces;
1175 }
1176
1177 static LWT_ELEMID*
cb_getRingEdges(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID edge,int * numelems,int limit)1178 cb_getRingEdges(const LWT_BE_TOPOLOGY* topo,
1179 LWT_ELEMID edge, int* numelems, int limit)
1180 {
1181 LWT_ELEMID *edges;
1182 int spi_result;
1183 TupleDesc rowdesc;
1184 StringInfoData sqldata;
1185 StringInfo sql = &sqldata;
1186 int i;
1187 MemoryContext oldcontext = CurrentMemoryContext;
1188
1189 initStringInfo(sql);
1190 appendStringInfo(sql, "WITH RECURSIVE edgering AS ( "
1191 "SELECT %" LWTFMT_ELEMID
1192 " as signed_edge_id, edge_id, next_left_edge, next_right_edge "
1193 "FROM \"%s\".edge_data WHERE edge_id = %" LWTFMT_ELEMID " UNION "
1194 "SELECT CASE WHEN "
1195 "p.signed_edge_id < 0 THEN p.next_right_edge ELSE p.next_left_edge END, "
1196 "e.edge_id, e.next_left_edge, e.next_right_edge "
1197 "FROM \"%s\".edge_data e, edgering p WHERE "
1198 "e.edge_id = CASE WHEN p.signed_edge_id < 0 THEN "
1199 "abs(p.next_right_edge) ELSE abs(p.next_left_edge) END ) "
1200 "SELECT * FROM edgering",
1201 edge, topo->name, ABS(edge), topo->name);
1202 if ( limit )
1203 {
1204 ++limit; /* so we know if we hit it */
1205 appendStringInfo(sql, " LIMIT %d", limit);
1206 }
1207
1208 POSTGIS_DEBUGF(1, "cb_getRingEdges query (limit %d): %s", limit, sql->data);
1209 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit);
1210 MemoryContextSwitchTo( oldcontext ); /* switch back */
1211 if ( spi_result != SPI_OK_SELECT )
1212 {
1213 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1214 pfree(sqldata.data);
1215 *numelems = -1;
1216 return NULL;
1217 }
1218 pfree(sqldata.data);
1219
1220 POSTGIS_DEBUGF(1, "cb_getRingEdges: edge query returned %d rows", SPI_processed);
1221 *numelems = SPI_processed;
1222 if ( ! SPI_processed )
1223 {
1224 return NULL;
1225 }
1226 if ( limit && *numelems == limit )
1227 {
1228 cberror(topo->be_data, "Max traversing limit hit: %d", limit-1);
1229 *numelems = -1;
1230 return NULL;
1231 }
1232
1233 edges = palloc( sizeof(LWT_ELEMID) * *numelems );
1234 rowdesc = SPI_tuptable->tupdesc;
1235 for ( i=0; i<*numelems; ++i )
1236 {
1237 HeapTuple row = SPI_tuptable->vals[i];
1238 bool isnull;
1239 Datum dat;
1240 int32 val;
1241 dat = SPI_getbinval(row, rowdesc, 1, &isnull);
1242 if ( isnull )
1243 {
1244 lwfree(edges);
1245 cberror(topo->be_data, "Found edge with NULL edge_id");
1246 *numelems = -1;
1247 return NULL;
1248 }
1249 val = DatumGetInt32(dat);
1250 edges[i] = val;
1251 POSTGIS_DEBUGF(1, "Component " UINT64_FORMAT " in ring of edge %" LWTFMT_ELEMID " is edge %d", i, edge, val);
1252
1253 /* For the last entry, check that we returned back to start
1254 * point, or complain about topology being corrupted */
1255 if ( i == *numelems - 1 )
1256 {
1257 int32 nextedge;
1258 int sidecol = val > 0 ? 3 : 4;
1259 const char *sidetext = val > 0 ? "left" : "right";
1260
1261 dat = SPI_getbinval(row, rowdesc, sidecol, &isnull);
1262 if ( isnull )
1263 {
1264 lwfree(edges);
1265 cberror(topo->be_data, "Edge %d" /*LWTFMT_ELEMID*/
1266 " has NULL next_%s_edge",
1267 val, sidetext);
1268 *numelems = -1;
1269 return NULL;
1270 }
1271 nextedge = DatumGetInt32(dat);
1272 POSTGIS_DEBUGF(1, "Last component in ring of edge %"
1273 LWTFMT_ELEMID " (%" LWTFMT_ELEMID ") has next_%s_edge %d",
1274 edge, val, sidetext, nextedge);
1275 if ( nextedge != edge )
1276 {
1277 lwfree(edges);
1278 cberror(topo->be_data, "Corrupted topology: ring of edge %"
1279 LWTFMT_ELEMID " is topologically non-closed",
1280 edge);
1281 *numelems = -1;
1282 return NULL;
1283 }
1284 }
1285
1286 }
1287
1288 SPI_freetuptable(SPI_tuptable);
1289
1290 return edges;
1291 }
1292
1293 static LWT_ISO_NODE*
cb_getNodeById(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields)1294 cb_getNodeById(const LWT_BE_TOPOLOGY* topo,
1295 const LWT_ELEMID* ids, int* numelems, int fields)
1296 {
1297 LWT_ISO_NODE *nodes;
1298 int spi_result;
1299
1300 StringInfoData sqldata;
1301 StringInfo sql = &sqldata;
1302 int i;
1303 MemoryContext oldcontext = CurrentMemoryContext;
1304
1305 initStringInfo(sql);
1306 appendStringInfoString(sql, "SELECT ");
1307 addNodeFields(sql, fields);
1308 appendStringInfo(sql, " FROM \"%s\".node", topo->name);
1309 appendStringInfoString(sql, " WHERE node_id IN (");
1310 // add all identifiers here
1311 for (i=0; i<*numelems; ++i)
1312 {
1313 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
1314 }
1315 appendStringInfoString(sql, ")");
1316 POSTGIS_DEBUGF(1, "cb_getNodeById query: %s", sql->data);
1317 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems);
1318 MemoryContextSwitchTo( oldcontext ); /* switch back */
1319 if ( spi_result != SPI_OK_SELECT )
1320 {
1321 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1322 pfree(sqldata.data);
1323 *numelems = -1;
1324 return NULL;
1325 }
1326 pfree(sqldata.data);
1327
1328 POSTGIS_DEBUGF(1, "cb_getNodeById: edge query returned %d rows", SPI_processed);
1329 *numelems = SPI_processed;
1330 if ( ! SPI_processed )
1331 {
1332 return NULL;
1333 }
1334
1335 nodes = palloc( sizeof(LWT_ISO_NODE) * *numelems );
1336 for ( i=0; i<*numelems; ++i )
1337 {
1338 HeapTuple row = SPI_tuptable->vals[i];
1339 fillNodeFields(&nodes[i], row, SPI_tuptable->tupdesc, fields);
1340 }
1341
1342 SPI_freetuptable(SPI_tuptable);
1343
1344 return nodes;
1345 }
1346
1347 static LWT_ISO_NODE*
cb_getNodeByFace(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int * numelems,int fields,const GBOX * box)1348 cb_getNodeByFace(const LWT_BE_TOPOLOGY* topo,
1349 const LWT_ELEMID* ids, int* numelems, int fields,
1350 const GBOX *box)
1351 {
1352 LWT_ISO_NODE *nodes;
1353 int spi_result;
1354 MemoryContext oldcontext = CurrentMemoryContext;
1355 StringInfoData sqldata;
1356 StringInfo sql = &sqldata;
1357 int i;
1358 char *hexbox;
1359
1360 initStringInfo(sql);
1361 appendStringInfoString(sql, "SELECT ");
1362 addNodeFields(sql, fields);
1363 appendStringInfo(sql, " FROM \"%s\".node", topo->name);
1364 appendStringInfoString(sql, " WHERE containing_face IN (");
1365 // add all identifiers here
1366 for (i=0; i<*numelems; ++i)
1367 {
1368 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
1369 }
1370 appendStringInfoString(sql, ")");
1371 if ( box )
1372 {
1373 hexbox = _box2d_to_hexwkb(box, topo->srid);
1374 appendStringInfo(sql, " AND geom && '%s'::geometry", hexbox);
1375 lwfree(hexbox);
1376 }
1377 POSTGIS_DEBUGF(1, "cb_getNodeByFace query: %s", sql->data);
1378 POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
1379 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
1380 MemoryContextSwitchTo( oldcontext ); /* switch back */
1381 if ( spi_result != SPI_OK_SELECT )
1382 {
1383 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1384 pfree(sqldata.data);
1385 *numelems = -1;
1386 return NULL;
1387 }
1388 pfree(sqldata.data);
1389
1390 POSTGIS_DEBUGF(1, "cb_getNodeByFace: edge query returned %d rows", SPI_processed);
1391 *numelems = SPI_processed;
1392 if ( ! SPI_processed )
1393 {
1394 return NULL;
1395 }
1396
1397 nodes = palloc( sizeof(LWT_ISO_NODE) * *numelems );
1398 for ( i=0; i<*numelems; ++i )
1399 {
1400 HeapTuple row = SPI_tuptable->vals[i];
1401 fillNodeFields(&nodes[i], row, SPI_tuptable->tupdesc, fields);
1402 }
1403
1404 SPI_freetuptable(SPI_tuptable);
1405
1406 return nodes;
1407 }
1408
1409 static LWT_ISO_EDGE*
cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY * topo,const LWPOINT * pt,double dist,int * numelems,int fields,int limit)1410 cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
1411 const LWPOINT* pt, double dist, int* numelems,
1412 int fields, int limit)
1413 {
1414 LWT_ISO_EDGE *edges;
1415 int spi_result;
1416 int elems_requested = limit;
1417 size_t hexewkb_size;
1418 char *hexewkb;
1419 MemoryContext oldcontext = CurrentMemoryContext;
1420 StringInfoData sqldata;
1421 StringInfo sql = &sqldata;
1422 int i;
1423
1424 initStringInfo(sql);
1425 if ( elems_requested == -1 )
1426 {
1427 appendStringInfoString(sql, "SELECT EXISTS ( SELECT 1");
1428 }
1429 else
1430 {
1431 appendStringInfoString(sql, "SELECT ");
1432 addEdgeFields(sql, fields, 0);
1433 }
1434 appendStringInfo(sql, " FROM \"%s\".edge_data", topo->name);
1435 // TODO: use binary cursor here ?
1436 hexewkb = lwgeom_to_hexwkb(lwpoint_as_lwgeom(pt), WKB_EXTENDED, &hexewkb_size);
1437 if ( dist )
1438 {
1439 appendStringInfo(sql, " WHERE ST_DWithin('%s'::geometry, geom, %g)", hexewkb, dist);
1440 }
1441 else
1442 {
1443 appendStringInfo(sql, " WHERE ST_Within('%s'::geometry, geom)", hexewkb);
1444 }
1445 lwfree(hexewkb);
1446 if ( elems_requested == -1 )
1447 {
1448 appendStringInfoString(sql, ")");
1449 }
1450 else if ( elems_requested > 0 )
1451 {
1452 appendStringInfo(sql, " LIMIT %d", elems_requested);
1453 }
1454 POSTGIS_DEBUGF(1, "cb_getEdgeWithinDistance2D: query is: %s", sql->data);
1455 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 0);
1456 MemoryContextSwitchTo( oldcontext ); /* switch back */
1457 if ( spi_result != SPI_OK_SELECT )
1458 {
1459 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
1460 pfree(sqldata.data);
1461 *numelems = -1;
1462 return NULL;
1463 }
1464 pfree(sqldata.data);
1465
1466 POSTGIS_DEBUGF(1, "cb_getEdgeWithinDistance2D: edge query "
1467 "(limited by %d) returned %d rows",
1468 elems_requested, SPI_processed);
1469 *numelems = SPI_processed;
1470 if ( ! SPI_processed )
1471 {
1472 return NULL;
1473 }
1474
1475 if ( elems_requested == -1 )
1476 {
1477 /* This was an EXISTS query */
1478 {
1479 Datum dat;
1480 bool isnull, exists;
1481 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
1482 exists = DatumGetBool(dat);
1483 *numelems = exists ? 1 : 0;
1484 POSTGIS_DEBUGF(1, "cb_getEdgeWithinDistance2D: exists ? %d", *numelems);
1485 }
1486
1487 SPI_freetuptable(SPI_tuptable);
1488
1489 return NULL;
1490 }
1491
1492 edges = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
1493 for ( i=0; i<*numelems; ++i )
1494 {
1495 HeapTuple row = SPI_tuptable->vals[i];
1496 fillEdgeFields(&edges[i], row, SPI_tuptable->tupdesc, fields);
1497 }
1498
1499 SPI_freetuptable(SPI_tuptable);
1500
1501 return edges;
1502 }
1503
1504 static LWT_ISO_NODE*
cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY * topo,const LWPOINT * pt,double dist,int * numelems,int fields,int limit)1505 cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
1506 const LWPOINT* pt, double dist, int* numelems,
1507 int fields, int limit)
1508 {
1509 MemoryContext oldcontext = CurrentMemoryContext;
1510 LWT_ISO_NODE *nodes;
1511 int spi_result;
1512 size_t hexewkb_size;
1513 char *hexewkb;
1514 StringInfoData sqldata;
1515 StringInfo sql = &sqldata;
1516 int elems_requested = limit;
1517 int i;
1518
1519 initStringInfo(sql);
1520 if ( elems_requested == -1 )
1521 {
1522 appendStringInfoString(sql, "SELECT EXISTS ( SELECT 1");
1523 }
1524 else
1525 {
1526 appendStringInfoString(sql, "SELECT ");
1527 if ( fields ) addNodeFields(sql, fields);
1528 else
1529 {
1530 lwpgwarning("liblwgeom-topo invoked 'getNodeWithinDistance2D' "
1531 "backend callback with limit=%d and no fields",
1532 elems_requested);
1533 appendStringInfo(sql, "*");
1534 }
1535 }
1536 appendStringInfo(sql, " FROM \"%s\".node", topo->name);
1537 // TODO: use binary cursor here ?
1538 hexewkb = lwgeom_to_hexwkb(lwpoint_as_lwgeom(pt), WKB_EXTENDED, &hexewkb_size);
1539 if ( dist )
1540 {
1541 appendStringInfo(sql, " WHERE ST_DWithin(geom, '%s'::geometry, %g)",
1542 hexewkb, dist);
1543 }
1544 else
1545 {
1546 appendStringInfo(sql, " WHERE ST_Equals(geom, '%s'::geometry)", hexewkb);
1547 }
1548 lwfree(hexewkb);
1549 if ( elems_requested == -1 )
1550 {
1551 appendStringInfoString(sql, ")");
1552 }
1553 else if ( elems_requested > 0 )
1554 {
1555 appendStringInfo(sql, " LIMIT %d", elems_requested);
1556 }
1557 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 0);
1558 MemoryContextSwitchTo( oldcontext ); /* switch back */
1559 if ( spi_result != SPI_OK_SELECT )
1560 {
1561 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1562 spi_result, sql->data);
1563 pfree(sqldata.data);
1564 *numelems = -1;
1565 return NULL;
1566 }
1567 pfree(sqldata.data);
1568
1569 POSTGIS_DEBUGF(1, "cb_getNodeWithinDistance2D: node query "
1570 "(limited by %d) returned %d rows",
1571 elems_requested, SPI_processed);
1572 if ( ! SPI_processed )
1573 {
1574 *numelems = 0;
1575 return NULL;
1576 }
1577
1578 if ( elems_requested == -1 )
1579 {
1580 /* This was an EXISTS query */
1581 {
1582 Datum dat;
1583 bool isnull, exists;
1584 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
1585 exists = DatumGetBool(dat);
1586 *numelems = exists ? 1 : 0;
1587 }
1588
1589 SPI_freetuptable(SPI_tuptable);
1590
1591 return NULL;
1592 }
1593 else
1594 {
1595 *numelems = SPI_processed;
1596 nodes = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
1597 for ( i=0; i<*numelems; ++i )
1598 {
1599 HeapTuple row = SPI_tuptable->vals[i];
1600 fillNodeFields(&nodes[i], row, SPI_tuptable->tupdesc, fields);
1601 }
1602
1603 SPI_freetuptable(SPI_tuptable);
1604
1605 return nodes;
1606 }
1607 }
1608
1609 static int
cb_insertNodes(const LWT_BE_TOPOLOGY * topo,LWT_ISO_NODE * nodes,int numelems)1610 cb_insertNodes( const LWT_BE_TOPOLOGY* topo,
1611 LWT_ISO_NODE* nodes, int numelems )
1612 {
1613 MemoryContext oldcontext = CurrentMemoryContext;
1614 int spi_result;
1615 StringInfoData sqldata;
1616 StringInfo sql = &sqldata;
1617 int i;
1618
1619 initStringInfo(sql);
1620 appendStringInfo(sql, "INSERT INTO \"%s\".node (", topo->name);
1621 addNodeFields(sql, LWT_COL_NODE_ALL);
1622 appendStringInfoString(sql, ") VALUES ");
1623 for ( i=0; i<numelems; ++i )
1624 {
1625 if ( i ) appendStringInfoString(sql, ",");
1626 // TODO: prepare and execute ?
1627 addNodeValues(sql, &nodes[i], LWT_COL_NODE_ALL);
1628 }
1629 appendStringInfoString(sql, " RETURNING node_id");
1630
1631 POSTGIS_DEBUGF(1, "cb_insertNodes query: %s", sql->data);
1632
1633 spi_result = SPI_execute(sql->data, false, numelems);
1634 MemoryContextSwitchTo( oldcontext ); /* switch back */
1635 if ( spi_result != SPI_OK_INSERT_RETURNING )
1636 {
1637 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1638 spi_result, sql->data);
1639 pfree(sqldata.data);
1640 return 0;
1641 }
1642 pfree(sqldata.data);
1643
1644 if ( SPI_processed ) topo->be_data->data_changed = true;
1645
1646 // TODO: Remove cast when numelems uses uint64 instead of int
1647 if ( SPI_processed != (uint64) numelems )
1648 {
1649 cberror(topo->be_data, "processed " UINT64_FORMAT " rows, expected %d",
1650 (uint64)SPI_processed, numelems);
1651 return 0;
1652 }
1653
1654 /* Set node_id (could skip this if none had it set to -1) */
1655 /* TODO: check for -1 values in the first loop */
1656 for ( i=0; i<numelems; ++i )
1657 {
1658 if ( nodes[i].node_id != -1 ) continue;
1659 fillNodeFields(&nodes[i], SPI_tuptable->vals[i],
1660 SPI_tuptable->tupdesc, LWT_COL_NODE_NODE_ID);
1661 }
1662
1663 SPI_freetuptable(SPI_tuptable);
1664
1665 return 1;
1666 }
1667
1668 static int
cb_insertEdges(const LWT_BE_TOPOLOGY * topo,LWT_ISO_EDGE * edges,int numelems)1669 cb_insertEdges( const LWT_BE_TOPOLOGY* topo,
1670 LWT_ISO_EDGE* edges, int numelems )
1671 {
1672 MemoryContext oldcontext = CurrentMemoryContext;
1673 int spi_result;
1674 StringInfoData sqldata;
1675 StringInfo sql = &sqldata;
1676 int i;
1677 int needsEdgeIdReturn = 0;
1678
1679 initStringInfo(sql);
1680 /* NOTE: we insert into "edge", on which an insert rule is defined */
1681 appendStringInfo(sql, "INSERT INTO \"%s\".edge_data (", topo->name);
1682 addEdgeFields(sql, LWT_COL_EDGE_ALL, 1);
1683 appendStringInfoString(sql, ") VALUES ");
1684 for ( i=0; i<numelems; ++i )
1685 {
1686 if ( i ) appendStringInfoString(sql, ",");
1687 // TODO: prepare and execute ?
1688 addEdgeValues(sql, &edges[i], LWT_COL_EDGE_ALL, 1);
1689 if ( edges[i].edge_id == -1 ) needsEdgeIdReturn = 1;
1690 }
1691 if ( needsEdgeIdReturn ) appendStringInfoString(sql, " RETURNING edge_id");
1692
1693 POSTGIS_DEBUGF(1, "cb_insertEdges query (%d elems): %s", numelems, sql->data);
1694 spi_result = SPI_execute(sql->data, false, numelems);
1695 MemoryContextSwitchTo( oldcontext ); /* switch back */
1696 if ( spi_result != ( needsEdgeIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) )
1697 {
1698 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1699 spi_result, sql->data);
1700 pfree(sqldata.data);
1701 return -1;
1702 }
1703 pfree(sqldata.data);
1704 if ( SPI_processed ) topo->be_data->data_changed = true;
1705 POSTGIS_DEBUGF(1, "cb_insertEdges query processed %d rows", SPI_processed);
1706 if ( SPI_processed != (uint64) numelems )
1707 {
1708 cberror(topo->be_data, "processed " UINT64_FORMAT " rows, expected %d",
1709 (uint64)SPI_processed, numelems);
1710 return -1;
1711 }
1712
1713 if ( needsEdgeIdReturn )
1714 {
1715 /* Set node_id for items that need it */
1716 for ( i=0; i<(int)SPI_processed; ++i )
1717 {
1718 if ( edges[i].edge_id != -1 ) continue;
1719 fillEdgeFields(&edges[i], SPI_tuptable->vals[i],
1720 SPI_tuptable->tupdesc, LWT_COL_EDGE_EDGE_ID);
1721 }
1722 }
1723
1724 SPI_freetuptable(SPI_tuptable);
1725
1726 return SPI_processed;
1727 }
1728
1729 static int
cb_insertFaces(const LWT_BE_TOPOLOGY * topo,LWT_ISO_FACE * faces,int numelems)1730 cb_insertFaces( const LWT_BE_TOPOLOGY* topo,
1731 LWT_ISO_FACE* faces, int numelems )
1732 {
1733 MemoryContext oldcontext = CurrentMemoryContext;
1734 int spi_result;
1735 StringInfoData sqldata;
1736 StringInfo sql = &sqldata;
1737 int i;
1738 int needsFaceIdReturn = 0;
1739
1740 initStringInfo(sql);
1741 appendStringInfo(sql, "INSERT INTO \"%s\".face (", topo->name);
1742 addFaceFields(sql, LWT_COL_FACE_ALL);
1743 appendStringInfoString(sql, ") VALUES ");
1744 for ( i=0; i<numelems; ++i )
1745 {
1746 if ( i ) appendStringInfoString(sql, ",");
1747 // TODO: prepare and execute ?
1748 addFaceValues(sql, &faces[i], topo->srid);
1749 if ( faces[i].face_id == -1 ) needsFaceIdReturn = 1;
1750 }
1751 if ( needsFaceIdReturn ) appendStringInfoString(sql, " RETURNING face_id");
1752
1753 POSTGIS_DEBUGF(1, "cb_insertFaces query (%d elems): %s", numelems, sql->data);
1754 spi_result = SPI_execute(sql->data, false, numelems);
1755 MemoryContextSwitchTo( oldcontext ); /* switch back */
1756 if ( spi_result != ( needsFaceIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) )
1757 {
1758 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1759 spi_result, sql->data);
1760 pfree(sqldata.data);
1761 return -1;
1762 }
1763 pfree(sqldata.data);
1764 if ( SPI_processed ) topo->be_data->data_changed = true;
1765 POSTGIS_DEBUGF(1, "cb_insertFaces query processed %d rows", SPI_processed);
1766 if ( SPI_processed != (uint64) numelems )
1767 {
1768 cberror(topo->be_data, "processed " UINT64_FORMAT " rows, expected %d",
1769 (uint64)SPI_processed, numelems);
1770 return -1;
1771 }
1772
1773 if ( needsFaceIdReturn )
1774 {
1775 /* Set node_id for items that need it */
1776 for ( i=0; i<numelems; ++i )
1777 {
1778 if ( faces[i].face_id != -1 ) continue;
1779 fillFaceFields(&faces[i], SPI_tuptable->vals[i],
1780 SPI_tuptable->tupdesc, LWT_COL_FACE_FACE_ID);
1781 }
1782 }
1783
1784 SPI_freetuptable(SPI_tuptable);
1785
1786 return SPI_processed;
1787 }
1788
1789 static int
cb_updateEdges(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_EDGE * sel_edge,int sel_fields,const LWT_ISO_EDGE * upd_edge,int upd_fields,const LWT_ISO_EDGE * exc_edge,int exc_fields)1790 cb_updateEdges( const LWT_BE_TOPOLOGY* topo,
1791 const LWT_ISO_EDGE* sel_edge, int sel_fields,
1792 const LWT_ISO_EDGE* upd_edge, int upd_fields,
1793 const LWT_ISO_EDGE* exc_edge, int exc_fields )
1794 {
1795 MemoryContext oldcontext = CurrentMemoryContext;
1796 int spi_result;
1797 StringInfoData sqldata;
1798 StringInfo sql = &sqldata;
1799
1800 initStringInfo(sql);
1801 appendStringInfo(sql, "UPDATE \"%s\".edge_data SET ", topo->name);
1802 addEdgeUpdate( sql, upd_edge, upd_fields, 1, updSet );
1803 if ( exc_edge || sel_edge ) appendStringInfoString(sql, " WHERE ");
1804 if ( sel_edge )
1805 {
1806 addEdgeUpdate( sql, sel_edge, sel_fields, 1, updSel );
1807 if ( exc_edge ) appendStringInfoString(sql, " AND ");
1808 }
1809 if ( exc_edge )
1810 {
1811 addEdgeUpdate( sql, exc_edge, exc_fields, 1, updNot );
1812 }
1813
1814 POSTGIS_DEBUGF(1, "cb_updateEdges query: %s", sql->data);
1815
1816 spi_result = SPI_execute( sql->data, false, 0 );
1817 MemoryContextSwitchTo( oldcontext ); /* switch back */
1818 if ( spi_result != SPI_OK_UPDATE )
1819 {
1820 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1821 spi_result, sql->data);
1822 pfree(sqldata.data);
1823 return -1;
1824 }
1825 pfree(sqldata.data);
1826
1827 if ( SPI_processed ) topo->be_data->data_changed = true;
1828
1829 POSTGIS_DEBUGF(1, "cb_updateEdges: update query processed %d rows", SPI_processed);
1830
1831 return SPI_processed;
1832 }
1833
1834 static int
cb_updateNodes(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_NODE * sel_node,int sel_fields,const LWT_ISO_NODE * upd_node,int upd_fields,const LWT_ISO_NODE * exc_node,int exc_fields)1835 cb_updateNodes( const LWT_BE_TOPOLOGY* topo,
1836 const LWT_ISO_NODE* sel_node, int sel_fields,
1837 const LWT_ISO_NODE* upd_node, int upd_fields,
1838 const LWT_ISO_NODE* exc_node, int exc_fields )
1839 {
1840 MemoryContext oldcontext = CurrentMemoryContext;
1841 int spi_result;
1842 StringInfoData sqldata;
1843 StringInfo sql = &sqldata;
1844
1845 initStringInfo(sql);
1846 appendStringInfo(sql, "UPDATE \"%s\".node SET ", topo->name);
1847 addNodeUpdate( sql, upd_node, upd_fields, 1, updSet );
1848 if ( exc_node || sel_node ) appendStringInfoString(sql, " WHERE ");
1849 if ( sel_node )
1850 {
1851 addNodeUpdate( sql, sel_node, sel_fields, 1, updSel );
1852 if ( exc_node ) appendStringInfoString(sql, " AND ");
1853 }
1854 if ( exc_node )
1855 {
1856 addNodeUpdate( sql, exc_node, exc_fields, 1, updNot );
1857 }
1858
1859 POSTGIS_DEBUGF(1, "cb_updateNodes: %s", sql->data);
1860
1861 spi_result = SPI_execute( sql->data, false, 0 );
1862 MemoryContextSwitchTo( oldcontext ); /* switch back */
1863 if ( spi_result != SPI_OK_UPDATE )
1864 {
1865 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1866 spi_result, sql->data);
1867 pfree(sqldata.data);
1868 return -1;
1869 }
1870 pfree(sqldata.data);
1871
1872 if ( SPI_processed ) topo->be_data->data_changed = true;
1873
1874 POSTGIS_DEBUGF(1, "cb_updateNodes: update query processed %d rows", SPI_processed);
1875
1876 return SPI_processed;
1877 }
1878
1879 static int
cb_updateNodesById(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_NODE * nodes,int numnodes,int fields)1880 cb_updateNodesById( const LWT_BE_TOPOLOGY* topo,
1881 const LWT_ISO_NODE* nodes, int numnodes, int fields )
1882 {
1883 MemoryContext oldcontext = CurrentMemoryContext;
1884 int i;
1885 int spi_result;
1886 StringInfoData sqldata;
1887 StringInfo sql = &sqldata;
1888 const char *sep = "";
1889 const char *sep1 = ",";
1890
1891 if ( ! fields )
1892 {
1893 cberror(topo->be_data,
1894 "updateNodesById callback called with no update fields!");
1895 return -1;
1896 }
1897
1898 POSTGIS_DEBUGF(1, "cb_updateNodesById got %d nodes to update"
1899 " (fields:%d)",
1900 numnodes, fields);
1901
1902 initStringInfo(sql);
1903 appendStringInfoString(sql, "WITH newnodes(node_id,");
1904 addNodeFields(sql, fields);
1905 appendStringInfoString(sql, ") AS ( VALUES ");
1906 for (i=0; i<numnodes; ++i)
1907 {
1908 const LWT_ISO_NODE* node = &(nodes[i]);
1909 if ( i ) appendStringInfoString(sql, ",");
1910 addNodeValues(sql, node, LWT_COL_NODE_NODE_ID|fields);
1911 }
1912 appendStringInfo(sql, " ) UPDATE \"%s\".node n SET ", topo->name);
1913
1914 /* TODO: turn the following into a function */
1915 if ( fields & LWT_COL_NODE_NODE_ID )
1916 {
1917 appendStringInfo(sql, "%snode_id = o.node_id", sep);
1918 sep = sep1;
1919 }
1920 if ( fields & LWT_COL_NODE_CONTAINING_FACE )
1921 {
1922 appendStringInfo(sql, "%scontaining_face = o.containing_face", sep);
1923 sep = sep1;
1924 }
1925 if ( fields & LWT_COL_NODE_GEOM )
1926 {
1927 appendStringInfo(sql, "%sgeom = o.geom", sep);
1928 }
1929
1930 appendStringInfo(sql, " FROM newnodes o WHERE n.node_id = o.node_id");
1931
1932 POSTGIS_DEBUGF(1, "cb_updateNodesById query: %s", sql->data);
1933
1934 spi_result = SPI_execute( sql->data, false, 0 );
1935 MemoryContextSwitchTo( oldcontext ); /* switch back */
1936 if ( spi_result != SPI_OK_UPDATE )
1937 {
1938 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1939 spi_result, sql->data);
1940 pfree(sqldata.data);
1941 return -1;
1942 }
1943 pfree(sqldata.data);
1944
1945 if ( SPI_processed ) topo->be_data->data_changed = true;
1946
1947 POSTGIS_DEBUGF(1, "cb_updateNodesById: update query processed %d rows", SPI_processed);
1948
1949 return SPI_processed;
1950 }
1951
1952 static int
cb_updateFacesById(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_FACE * faces,int numfaces)1953 cb_updateFacesById( const LWT_BE_TOPOLOGY* topo,
1954 const LWT_ISO_FACE* faces, int numfaces )
1955 {
1956 MemoryContext oldcontext = CurrentMemoryContext;
1957 int i;
1958 int spi_result;
1959 StringInfoData sqldata;
1960 StringInfo sql = &sqldata;
1961
1962 initStringInfo(sql);
1963 appendStringInfoString(sql, "WITH newfaces(id,mbr) AS ( VALUES ");
1964 for (i=0; i<numfaces; ++i)
1965 {
1966 const LWT_ISO_FACE* face = &(faces[i]);
1967 char *hexbox = _box2d_to_hexwkb(face->mbr, topo->srid);
1968
1969 if ( i ) appendStringInfoChar(sql, ',');
1970
1971 appendStringInfo(sql, "(%" LWTFMT_ELEMID
1972 ", ST_Envelope('%s'::geometry))",
1973 face->face_id, hexbox);
1974 lwfree(hexbox);
1975 }
1976 appendStringInfo(sql, ") UPDATE \"%s\".face o SET mbr = i.mbr "
1977 "FROM newfaces i WHERE o.face_id = i.id",
1978 topo->name);
1979
1980 POSTGIS_DEBUGF(1, "cb_updateFacesById query: %s", sql->data);
1981
1982 spi_result = SPI_execute( sql->data, false, 0 );
1983 MemoryContextSwitchTo( oldcontext ); /* switch back */
1984 if ( spi_result != SPI_OK_UPDATE )
1985 {
1986 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
1987 spi_result, sql->data);
1988 pfree(sqldata.data);
1989 return -1;
1990 }
1991 pfree(sqldata.data);
1992
1993 if ( SPI_processed ) topo->be_data->data_changed = true;
1994
1995 POSTGIS_DEBUGF(1, "cb_updateFacesById: update query processed %d rows", SPI_processed);
1996
1997 return SPI_processed;
1998 }
1999
2000 static int
cb_updateEdgesById(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_EDGE * edges,int numedges,int fields)2001 cb_updateEdgesById( const LWT_BE_TOPOLOGY* topo,
2002 const LWT_ISO_EDGE* edges, int numedges, int fields )
2003 {
2004 MemoryContext oldcontext = CurrentMemoryContext;
2005 int i;
2006 int spi_result;
2007 StringInfoData sqldata;
2008 StringInfo sql = &sqldata;
2009 const char *sep = "";
2010 const char *sep1 = ",";
2011
2012 if ( ! fields )
2013 {
2014 cberror(topo->be_data,
2015 "updateEdgesById callback called with no update fields!");
2016 return -1;
2017 }
2018
2019 initStringInfo(sql);
2020 appendStringInfoString(sql, "WITH newedges(edge_id,");
2021 addEdgeFields(sql, fields, 0);
2022 appendStringInfoString(sql, ") AS ( VALUES ");
2023 for (i=0; i<numedges; ++i)
2024 {
2025 const LWT_ISO_EDGE* edge = &(edges[i]);
2026 if ( i ) appendStringInfoString(sql, ",");
2027 addEdgeValues(sql, edge, fields|LWT_COL_EDGE_EDGE_ID, 0);
2028 }
2029 appendStringInfo(sql, ") UPDATE \"%s\".edge_data e SET ", topo->name);
2030
2031 /* TODO: turn the following into a function */
2032 if ( fields & LWT_COL_EDGE_START_NODE )
2033 {
2034 appendStringInfo(sql, "%sstart_node = o.start_node", sep);
2035 sep = sep1;
2036 }
2037 if ( fields & LWT_COL_EDGE_END_NODE )
2038 {
2039 appendStringInfo(sql, "%send_node = o.end_node", sep);
2040 sep = sep1;
2041 }
2042 if ( fields & LWT_COL_EDGE_FACE_LEFT )
2043 {
2044 appendStringInfo(sql, "%sleft_face = o.left_face", sep);
2045 sep = sep1;
2046 }
2047 if ( fields & LWT_COL_EDGE_FACE_RIGHT )
2048 {
2049 appendStringInfo(sql, "%sright_face = o.right_face", sep);
2050 sep = sep1;
2051 }
2052 if ( fields & LWT_COL_EDGE_NEXT_LEFT )
2053 {
2054 appendStringInfo(sql,
2055 "%snext_left_edge = o.next_left_edge, "
2056 "abs_next_left_edge = abs(o.next_left_edge)", sep);
2057 sep = sep1;
2058 }
2059 if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
2060 {
2061 appendStringInfo(sql,
2062 "%snext_right_edge = o.next_right_edge, "
2063 "abs_next_right_edge = abs(o.next_right_edge)", sep);
2064 sep = sep1;
2065 }
2066 if ( fields & LWT_COL_EDGE_GEOM )
2067 {
2068 appendStringInfo(sql, "%sgeom = o.geom", sep);
2069 }
2070
2071 appendStringInfo(sql, " FROM newedges o WHERE e.edge_id = o.edge_id");
2072
2073 POSTGIS_DEBUGF(1, "cb_updateEdgesById query: %s", sql->data);
2074
2075 spi_result = SPI_execute( sql->data, false, 0 );
2076 MemoryContextSwitchTo( oldcontext ); /* switch back */
2077 if ( spi_result != SPI_OK_UPDATE )
2078 {
2079 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2080 spi_result, sql->data);
2081 pfree(sqldata.data);
2082 return -1;
2083 }
2084 pfree(sqldata.data);
2085
2086 if ( SPI_processed ) topo->be_data->data_changed = true;
2087
2088 POSTGIS_DEBUGF(1, "cb_updateEdgesById: update query processed %d rows", SPI_processed);
2089
2090 return SPI_processed;
2091 }
2092
2093 static int
cb_deleteEdges(const LWT_BE_TOPOLOGY * topo,const LWT_ISO_EDGE * sel_edge,int sel_fields)2094 cb_deleteEdges( const LWT_BE_TOPOLOGY* topo,
2095 const LWT_ISO_EDGE* sel_edge, int sel_fields )
2096 {
2097 MemoryContext oldcontext = CurrentMemoryContext;
2098 int spi_result;
2099 StringInfoData sqldata;
2100 StringInfo sql = &sqldata;
2101
2102 initStringInfo(sql);
2103 appendStringInfo(sql, "DELETE FROM \"%s\".edge_data WHERE ", topo->name);
2104 addEdgeUpdate( sql, sel_edge, sel_fields, 0, updSel );
2105
2106 POSTGIS_DEBUGF(1, "cb_deleteEdges: %s", sql->data);
2107
2108 spi_result = SPI_execute( sql->data, false, 0 );
2109 MemoryContextSwitchTo( oldcontext ); /* switch back */
2110 if ( spi_result != SPI_OK_DELETE )
2111 {
2112 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2113 spi_result, sql->data);
2114 pfree(sqldata.data);
2115 return -1;
2116 }
2117 pfree(sqldata.data);
2118
2119 if ( SPI_processed ) topo->be_data->data_changed = true;
2120
2121 POSTGIS_DEBUGF(1, "cb_deleteEdges: delete query processed %d rows", SPI_processed);
2122
2123 return SPI_processed;
2124 }
2125
2126 static LWT_ELEMID
cb_getNextEdgeId(const LWT_BE_TOPOLOGY * topo)2127 cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo )
2128 {
2129 MemoryContext oldcontext = CurrentMemoryContext;
2130 int spi_result;
2131 StringInfoData sqldata;
2132 StringInfo sql = &sqldata;
2133 bool isnull;
2134 Datum dat;
2135 LWT_ELEMID edge_id;
2136
2137 initStringInfo(sql);
2138 appendStringInfo(sql, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
2139 topo->name);
2140 spi_result = SPI_execute(sql->data, false, 0);
2141 MemoryContextSwitchTo( oldcontext ); /* switch back */
2142 if ( spi_result != SPI_OK_SELECT )
2143 {
2144 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2145 spi_result, sql->data);
2146 pfree(sqldata.data);
2147 return -1;
2148 }
2149 pfree(sqldata.data);
2150
2151 if ( SPI_processed ) topo->be_data->data_changed = true;
2152
2153 if ( SPI_processed != 1 )
2154 {
2155 cberror(topo->be_data, "processed " UINT64_FORMAT " rows, expected 1",
2156 (uint64)SPI_processed);
2157 return -1;
2158 }
2159
2160 dat = SPI_getbinval( SPI_tuptable->vals[0],
2161 SPI_tuptable->tupdesc, 1, &isnull );
2162 if ( isnull )
2163 {
2164 cberror(topo->be_data, "nextval for edge_id returned null");
2165 return -1;
2166 }
2167 edge_id = DatumGetInt64(dat); /* sequences return 64bit integers */
2168
2169 SPI_freetuptable(SPI_tuptable);
2170
2171 return edge_id;
2172 }
2173
2174 static int
cb_updateTopoGeomEdgeSplit(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID split_edge,LWT_ELEMID new_edge1,LWT_ELEMID new_edge2)2175 cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
2176 LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2 )
2177 {
2178 MemoryContext oldcontext = CurrentMemoryContext;
2179 int spi_result;
2180 StringInfoData sqldata;
2181 StringInfo sql = &sqldata;
2182 int i, ntopogeoms;
2183 const char *proj = "r.element_id, r.topogeo_id, r.layer_id, r.element_type";
2184
2185 initStringInfo(sql);
2186 if ( new_edge2 == -1 )
2187 {
2188 appendStringInfo(sql, "SELECT %s", proj);
2189 }
2190 else
2191 {
2192 appendStringInfoString(sql, "DELETE");
2193 }
2194 appendStringInfo( sql, " FROM \"%s\".relation r %s topology.layer l WHERE "
2195 "l.topology_id = %d AND l.level = 0 AND l.layer_id = r.layer_id "
2196 "AND abs(r.element_id) = %" LWTFMT_ELEMID " AND r.element_type = 2",
2197 topo->name, (new_edge2 == -1 ? "," : "USING" ), topo->id, split_edge );
2198 if ( new_edge2 != -1 )
2199 {
2200 appendStringInfo(sql, " RETURNING %s", proj);
2201 }
2202
2203 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit query: %s", sql->data);
2204
2205 spi_result = SPI_execute(sql->data, new_edge2 == -1 ? !topo->be_data->data_changed : false, 0);
2206 MemoryContextSwitchTo( oldcontext ); /* switch back */
2207 if ( spi_result != ( new_edge2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) )
2208 {
2209 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2210 spi_result, sql->data);
2211 pfree(sqldata.data);
2212 return 0;
2213 }
2214
2215 if ( spi_result == SPI_OK_DELETE_RETURNING && SPI_processed )
2216 {
2217 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit: deleted %d faces", SPI_processed);
2218 topo->be_data->data_changed = true;
2219 }
2220
2221 ntopogeoms = SPI_processed;
2222 if ( ntopogeoms )
2223 {
2224 resetStringInfo(sql);
2225 appendStringInfo(sql, "INSERT INTO \"%s\".relation VALUES ", topo->name);
2226 for ( i=0; i<ntopogeoms; ++i )
2227 {
2228 HeapTuple row = SPI_tuptable->vals[i];
2229 TupleDesc tdesc = SPI_tuptable->tupdesc;
2230 int negate;
2231 int element_id;
2232 int topogeo_id;
2233 int layer_id;
2234 int element_type;
2235
2236 if ( ! getNotNullInt32( row, tdesc, 1, &element_id ) )
2237 {
2238 cberror(topo->be_data,
2239 "unexpected null element_id in \"%s\".relation",
2240 topo->name);
2241 return 0;
2242 }
2243 negate = ( element_id < 0 );
2244
2245 if ( ! getNotNullInt32( row, tdesc, 2, &topogeo_id ) )
2246 {
2247 cberror(topo->be_data,
2248 "unexpected null topogeo_id in \"%s\".relation",
2249 topo->name);
2250 return 0;
2251 }
2252
2253 if ( ! getNotNullInt32( row, tdesc, 3, &layer_id ) )
2254 {
2255 cberror(topo->be_data,
2256 "unexpected null layer_id in \"%s\".relation",
2257 topo->name);
2258 return 0;
2259 }
2260
2261 if ( ! getNotNullInt32( row, tdesc, 4, &element_type ) )
2262 {
2263 cberror(topo->be_data,
2264 "unexpected null element_type in \"%s\".relation",
2265 topo->name);
2266 return 0;
2267 }
2268
2269 if ( i ) appendStringInfoChar(sql, ',');
2270 appendStringInfo(sql, "(%d,%d,%" LWTFMT_ELEMID ",%d)",
2271 topogeo_id, layer_id, negate ? -new_edge1 : new_edge1, element_type);
2272 if ( new_edge2 != -1 )
2273 {
2274 resetStringInfo(sql);
2275 appendStringInfo(sql,
2276 ",VALUES (%d,%d,%" LWTFMT_ELEMID ",%d",
2277 topogeo_id, layer_id, negate ? -new_edge2 : new_edge2, element_type);
2278 }
2279 }
2280
2281 SPI_freetuptable(SPI_tuptable);
2282
2283 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit query: %s", sql->data);
2284 spi_result = SPI_execute(sql->data, false, 0);
2285 MemoryContextSwitchTo( oldcontext ); /* switch back */
2286 if ( spi_result != SPI_OK_INSERT )
2287 {
2288 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2289 spi_result, sql->data);
2290 pfree(sqldata.data);
2291 return 0;
2292 }
2293 if ( SPI_processed ) topo->be_data->data_changed = true;
2294 }
2295
2296 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit: updated %d topogeoms", ntopogeoms);
2297
2298 pfree(sqldata.data);
2299 return 1;
2300 }
2301
2302 static int
cb_updateTopoGeomFaceSplit(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID split_face,LWT_ELEMID new_face1,LWT_ELEMID new_face2)2303 cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
2304 LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2 )
2305 {
2306 MemoryContext oldcontext = CurrentMemoryContext;
2307 int spi_result;
2308 StringInfoData sqldata;
2309 StringInfo sql = &sqldata;
2310 int i, ntopogeoms;
2311 const char *proj = "r.element_id, r.topogeo_id, r.layer_id, r.element_type";
2312
2313 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit signalled "
2314 "split of face %" LWTFMT_ELEMID " into %"
2315 LWTFMT_ELEMID " and %" LWTFMT_ELEMID,
2316 split_face, new_face1, new_face2);
2317
2318 initStringInfo(sql);
2319 if ( new_face2 == -1 )
2320 {
2321 appendStringInfo(sql, "SELECT %s", proj);
2322 }
2323 else
2324 {
2325 appendStringInfoString(sql, "DELETE");
2326 }
2327 appendStringInfo( sql, " FROM \"%s\".relation r %s topology.layer l WHERE "
2328 "l.topology_id = %d AND l.level = 0 AND l.layer_id = r.layer_id "
2329 "AND abs(r.element_id) = %" LWTFMT_ELEMID " AND r.element_type = 3",
2330 topo->name, (new_face2 == -1 ? "," : "USING" ), topo->id, split_face );
2331 if ( new_face2 != -1 )
2332 {
2333 appendStringInfo(sql, " RETURNING %s", proj);
2334 }
2335
2336 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data);
2337
2338 spi_result = SPI_execute(sql->data, new_face2 == -1 ? !topo->be_data->data_changed : false, 0);
2339 MemoryContextSwitchTo( oldcontext ); /* switch back */
2340 if ( spi_result != ( new_face2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) )
2341 {
2342 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2343 spi_result, sql->data);
2344 pfree(sqldata.data);
2345 return 0;
2346 }
2347
2348 if ( spi_result == SPI_OK_DELETE_RETURNING && SPI_processed )
2349 {
2350 topo->be_data->data_changed = true;
2351 }
2352
2353 ntopogeoms = SPI_processed;
2354 if ( ntopogeoms )
2355 {
2356 resetStringInfo(sql);
2357 appendStringInfo(sql, "INSERT INTO \"%s\".relation VALUES ", topo->name);
2358 for ( i=0; i<ntopogeoms; ++i )
2359 {
2360 HeapTuple row = SPI_tuptable->vals[i];
2361 TupleDesc tdesc = SPI_tuptable->tupdesc;
2362 int negate;
2363 int element_id;
2364 int topogeo_id;
2365 int layer_id;
2366 int element_type;
2367
2368 if ( ! getNotNullInt32( row, tdesc, 1, &element_id ) )
2369 {
2370 cberror(topo->be_data,
2371 "unexpected null element_id in \"%s\".relation",
2372 topo->name);
2373 return 0;
2374 }
2375 negate = ( element_id < 0 );
2376
2377 if ( ! getNotNullInt32( row, tdesc, 2, &topogeo_id ) )
2378 {
2379 cberror(topo->be_data,
2380 "unexpected null topogeo_id in \"%s\".relation",
2381 topo->name);
2382 return 0;
2383 }
2384
2385 if ( ! getNotNullInt32( row, tdesc, 3, &layer_id ) )
2386 {
2387 cberror(topo->be_data,
2388 "unexpected null layer_id in \"%s\".relation",
2389 topo->name);
2390 return 0;
2391 }
2392
2393 if ( ! getNotNullInt32( row, tdesc, 4, &element_type ) )
2394 {
2395 cberror(topo->be_data,
2396 "unexpected null element_type in \"%s\".relation",
2397 topo->name);
2398 return 0;
2399 }
2400
2401 if ( i ) appendStringInfoChar(sql, ',');
2402 appendStringInfo(sql,
2403 "(%d,%d,%" LWTFMT_ELEMID ",%d)",
2404 topogeo_id, layer_id, negate ? -new_face1 : new_face1, element_type);
2405
2406 if ( new_face2 != -1 )
2407 {
2408 appendStringInfo(sql,
2409 ",(%d,%d,%" LWTFMT_ELEMID ",%d)",
2410 topogeo_id, layer_id, negate ? -new_face2 : new_face2, element_type);
2411 }
2412 }
2413
2414 SPI_freetuptable(SPI_tuptable);
2415
2416 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data);
2417 spi_result = SPI_execute(sql->data, false, 0);
2418 MemoryContextSwitchTo( oldcontext ); /* switch back */
2419 if ( spi_result != SPI_OK_INSERT )
2420 {
2421 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2422 spi_result, sql->data);
2423 pfree(sqldata.data);
2424 return 0;
2425 }
2426
2427 if ( SPI_processed ) topo->be_data->data_changed = true;
2428 }
2429
2430 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit: updated %d topogeoms", ntopogeoms);
2431
2432 pfree(sqldata.data);
2433 return 1;
2434 }
2435
2436 static int
cb_checkTopoGeomRemEdge(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID rem_edge,LWT_ELEMID face_left,LWT_ELEMID face_right)2437 cb_checkTopoGeomRemEdge ( const LWT_BE_TOPOLOGY* topo,
2438 LWT_ELEMID rem_edge, LWT_ELEMID face_left, LWT_ELEMID face_right )
2439 {
2440 MemoryContext oldcontext = CurrentMemoryContext;
2441 int spi_result;
2442 StringInfoData sqldata;
2443 StringInfo sql = &sqldata;
2444 const char *tg_id, *layer_id;
2445 const char *schema_name, *table_name, *col_name;
2446 HeapTuple row;
2447 TupleDesc tdesc;
2448
2449 POSTGIS_DEBUG(1, "cb_checkTopoGeomRemEdge enter ");
2450
2451 initStringInfo(sql);
2452 appendStringInfo( sql, "SELECT r.topogeo_id, r.layer_id, "
2453 "l.schema_name, l.table_name, l.feature_column FROM "
2454 "topology.layer l INNER JOIN \"%s\".relation r "
2455 "ON (l.layer_id = r.layer_id) WHERE l.level = 0 AND "
2456 "l.feature_type = 2 AND l.topology_id = %d"
2457 " AND abs(r.element_id) = %" LWTFMT_ELEMID,
2458 topo->name, topo->id, rem_edge );
2459
2460 POSTGIS_DEBUGF(1, "cb_checkTopoGeomRemEdge query 1: %s", sql->data);
2461
2462 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
2463 MemoryContextSwitchTo( oldcontext ); /* switch back */
2464 if ( spi_result != SPI_OK_SELECT )
2465 {
2466 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2467 spi_result, sql->data);
2468 pfree(sqldata.data);
2469 return 0;
2470 }
2471
2472 if ( SPI_processed )
2473 {
2474 row = SPI_tuptable->vals[0];
2475 tdesc = SPI_tuptable->tupdesc;
2476
2477 tg_id = SPI_getvalue(row, tdesc, 1);
2478 layer_id = SPI_getvalue(row, tdesc, 2);
2479 schema_name = SPI_getvalue(row, tdesc, 3);
2480 table_name = SPI_getvalue(row, tdesc, 4);
2481 col_name = SPI_getvalue(row, tdesc, 5);
2482
2483 SPI_freetuptable(SPI_tuptable);
2484
2485 cberror(topo->be_data, "TopoGeom %s in layer %s "
2486 "(%s.%s.%s) cannot be represented "
2487 "dropping edge %" LWTFMT_ELEMID,
2488 tg_id, layer_id, schema_name, table_name,
2489 col_name, rem_edge);
2490 return 0;
2491 }
2492
2493
2494 if ( face_left != face_right )
2495 {
2496 POSTGIS_DEBUGF(1, "Deletion of edge %" LWTFMT_ELEMID " joins faces %"
2497 LWTFMT_ELEMID " and %" LWTFMT_ELEMID,
2498 rem_edge, face_left, face_right);
2499 /*
2500 check if any topo_geom is defined only by one of the
2501 joined faces. In such case there would be no way to adapt
2502 the definition in case of healing, so we'd have to bail out
2503 */
2504 initStringInfo(sql);
2505 appendStringInfo( sql, "SELECT t.* FROM ( SELECT r.topogeo_id, "
2506 "r.layer_id, l.schema_name, l.table_name, l.feature_column, "
2507 "array_agg(r.element_id) as elems FROM topology.layer l "
2508 " INNER JOIN \"%s\".relation r ON (l.layer_id = r.layer_id) "
2509 "WHERE l.level = 0 and l.feature_type = 3 "
2510 "AND l.topology_id = %d"
2511 " AND r.element_id = ANY (ARRAY[%" LWTFMT_ELEMID ",%" LWTFMT_ELEMID
2512 "]::int4[]) group by r.topogeo_id, r.layer_id, l.schema_name, "
2513 "l.table_name, l.feature_column ) t WHERE NOT t.elems @> ARRAY[%"
2514 LWTFMT_ELEMID ",%" LWTFMT_ELEMID "]::int4[]",
2515
2516 topo->name, topo->id,
2517 face_left, face_right, face_left, face_right );
2518
2519 POSTGIS_DEBUGF(1, "cb_checkTopoGeomRemEdge query 2: %s", sql->data);
2520 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
2521 MemoryContextSwitchTo( oldcontext ); /* switch back */
2522
2523 if ( spi_result != SPI_OK_SELECT )
2524 {
2525 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2526 spi_result, sql->data);
2527 pfree(sqldata.data);
2528 return 0;
2529 }
2530
2531 if ( SPI_processed )
2532 {
2533 row = SPI_tuptable->vals[0];
2534 tdesc = SPI_tuptable->tupdesc;
2535
2536 tg_id = SPI_getvalue(row, tdesc, 1);
2537 layer_id = SPI_getvalue(row, tdesc, 2);
2538 schema_name = SPI_getvalue(row, tdesc, 3);
2539 table_name = SPI_getvalue(row, tdesc, 4);
2540 col_name = SPI_getvalue(row, tdesc, 5);
2541
2542 SPI_freetuptable(SPI_tuptable);
2543
2544 cberror(topo->be_data, "TopoGeom %s in layer %s "
2545 "(%s.%s.%s) cannot be represented "
2546 "healing faces %" LWTFMT_ELEMID
2547 " and %" LWTFMT_ELEMID,
2548 tg_id, layer_id, schema_name, table_name,
2549 col_name, face_right, face_left);
2550 return 0;
2551 }
2552 }
2553
2554 return 1;
2555 }
2556
2557 static int
cb_checkTopoGeomRemNode(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID rem_node,LWT_ELEMID edge1,LWT_ELEMID edge2)2558 cb_checkTopoGeomRemNode ( const LWT_BE_TOPOLOGY* topo,
2559 LWT_ELEMID rem_node, LWT_ELEMID edge1, LWT_ELEMID edge2 )
2560 {
2561 MemoryContext oldcontext = CurrentMemoryContext;
2562 int spi_result;
2563 StringInfoData sqldata;
2564 StringInfo sql = &sqldata;
2565 const char *tg_id, *layer_id;
2566 const char *schema_name, *table_name, *col_name;
2567 HeapTuple row;
2568 TupleDesc tdesc;
2569
2570 initStringInfo(sql);
2571 appendStringInfo( sql, "SELECT t.* FROM ( SELECT r.topogeo_id, "
2572 "r.layer_id, l.schema_name, l.table_name, l.feature_column, "
2573 "array_agg(abs(r.element_id)) as elems FROM topology.layer l "
2574 " INNER JOIN \"%s\".relation r ON (l.layer_id = r.layer_id) "
2575 "WHERE l.level = 0 and l.feature_type = 2 "
2576 "AND l.topology_id = %d"
2577 " AND abs(r.element_id) = ANY (ARRAY[%" LWTFMT_ELEMID ",%" LWTFMT_ELEMID
2578 "]::int4[]) group by r.topogeo_id, r.layer_id, l.schema_name, "
2579 "l.table_name, l.feature_column ) t WHERE NOT t.elems @> ARRAY[%"
2580 LWTFMT_ELEMID ",%" LWTFMT_ELEMID "]::int4[]",
2581 topo->name, topo->id,
2582 edge1, edge2, edge1, edge2 );
2583
2584 POSTGIS_DEBUGF(1, "cb_checkTopoGeomRemNode query 1: %s", sql->data);
2585
2586 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
2587 MemoryContextSwitchTo( oldcontext ); /* switch back */
2588 if ( spi_result != SPI_OK_SELECT )
2589 {
2590 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2591 spi_result, sql->data);
2592 pfree(sqldata.data);
2593 return 0;
2594 }
2595
2596 if ( SPI_processed )
2597 {
2598 row = SPI_tuptable->vals[0];
2599 tdesc = SPI_tuptable->tupdesc;
2600
2601 tg_id = SPI_getvalue(row, tdesc, 1);
2602 layer_id = SPI_getvalue(row, tdesc, 2);
2603 schema_name = SPI_getvalue(row, tdesc, 3);
2604 table_name = SPI_getvalue(row, tdesc, 4);
2605 col_name = SPI_getvalue(row, tdesc, 5);
2606
2607 SPI_freetuptable(SPI_tuptable);
2608
2609 cberror(topo->be_data, "TopoGeom %s in layer %s "
2610 "(%s.%s.%s) cannot be represented "
2611 "healing edges %" LWTFMT_ELEMID
2612 " and %" LWTFMT_ELEMID,
2613 tg_id, layer_id, schema_name, table_name,
2614 col_name, edge1, edge2);
2615 return 0;
2616 }
2617
2618 /* TODO: check for TopoGeometry objects being defined by the common
2619 * node, see https://trac.osgeo.org/postgis/ticket/3239 */
2620
2621 return 1;
2622 }
2623
2624 static int
cb_updateTopoGeomFaceHeal(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID face1,LWT_ELEMID face2,LWT_ELEMID newface)2625 cb_updateTopoGeomFaceHeal ( const LWT_BE_TOPOLOGY* topo,
2626 LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface )
2627 {
2628 MemoryContext oldcontext = CurrentMemoryContext;
2629 int spi_result;
2630 StringInfoData sqldata;
2631 StringInfo sql = &sqldata;
2632
2633 POSTGIS_DEBUG(1, "cb_updateTopoGeomFaceHeal enter ");
2634
2635 /* delete oldfaces (not equal to newface) from the
2636 * set of primitives defining the TopoGeometries found before */
2637
2638 if ( newface == face1 || newface == face2 )
2639 {
2640 initStringInfo(sql);
2641 /* this query can be optimized */
2642 appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
2643 "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 3"
2644 " AND l.topology_id = %d AND l.layer_id = r.layer_id "
2645 " AND abs(r.element_id) IN ( %" LWTFMT_ELEMID ",%" LWTFMT_ELEMID ")"
2646 " AND abs(r.element_id) != %" LWTFMT_ELEMID,
2647 topo->name, topo->id, face1, face2, newface );
2648 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query: %s", sql->data);
2649
2650 spi_result = SPI_execute(sql->data, false, 0);
2651 MemoryContextSwitchTo( oldcontext ); /* switch back */
2652 if ( spi_result != SPI_OK_DELETE )
2653 {
2654 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2655 spi_result, sql->data);
2656 pfree(sqldata.data);
2657 return 0;
2658 }
2659 if ( SPI_processed ) topo->be_data->data_changed = true;
2660 }
2661 else
2662 {
2663 initStringInfo(sql);
2664 /* delete face1 */
2665 appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
2666 "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 3"
2667 " AND l.topology_id = %d AND l.layer_id = r.layer_id "
2668 " AND abs(r.element_id) = %" LWTFMT_ELEMID,
2669 topo->name, topo->id, face1 );
2670 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 1: %s", sql->data);
2671
2672 spi_result = SPI_execute(sql->data, false, 0);
2673 MemoryContextSwitchTo( oldcontext ); /* switch back */
2674 if ( spi_result != SPI_OK_DELETE )
2675 {
2676 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2677 spi_result, sql->data);
2678 pfree(sqldata.data);
2679 return 0;
2680 }
2681 if ( SPI_processed ) topo->be_data->data_changed = true;
2682
2683 initStringInfo(sql);
2684 /* update face2 to newface */
2685 appendStringInfo( sql, "UPDATE \"%s\".relation r "
2686 "SET element_id = %" LWTFMT_ELEMID " FROM topology.layer l "
2687 "WHERE l.level = 0 AND l.feature_type = 3 AND l.topology_id = %d"
2688 " AND l.layer_id = r.layer_id AND r.element_id = %" LWTFMT_ELEMID,
2689 topo->name, newface, topo->id, face2 );
2690 POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 2: %s", sql->data);
2691
2692 spi_result = SPI_execute(sql->data, false, 0);
2693 MemoryContextSwitchTo( oldcontext ); /* switch back */
2694 if ( spi_result != SPI_OK_UPDATE )
2695 {
2696 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2697 spi_result, sql->data);
2698 pfree(sqldata.data);
2699 return 0;
2700 }
2701 if ( SPI_processed ) topo->be_data->data_changed = true;
2702 }
2703
2704 return 1;
2705 }
2706
2707 static int
cb_updateTopoGeomEdgeHeal(const LWT_BE_TOPOLOGY * topo,LWT_ELEMID edge1,LWT_ELEMID edge2,LWT_ELEMID newedge)2708 cb_updateTopoGeomEdgeHeal ( const LWT_BE_TOPOLOGY* topo,
2709 LWT_ELEMID edge1, LWT_ELEMID edge2, LWT_ELEMID newedge )
2710 {
2711 MemoryContext oldcontext = CurrentMemoryContext;
2712 int spi_result;
2713 StringInfoData sqldata;
2714 StringInfo sql = &sqldata;
2715
2716 /* delete old edges (not equal to new edge) from the
2717 * set of primitives defining the TopoGeometries found before */
2718
2719 if ( newedge == edge1 || newedge == edge2 )
2720 {
2721 initStringInfo(sql);
2722 /* this query can be optimized */
2723 appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
2724 "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 2"
2725 " AND l.topology_id = %d AND l.layer_id = r.layer_id "
2726 " AND abs(r.element_id) IN ( %" LWTFMT_ELEMID ",%" LWTFMT_ELEMID ")"
2727 " AND abs(r.element_id) != %" LWTFMT_ELEMID,
2728 topo->name, topo->id, edge1, edge2, newedge );
2729 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeHeal query: %s", sql->data);
2730
2731 spi_result = SPI_execute(sql->data, false, 0);
2732 MemoryContextSwitchTo( oldcontext ); /* switch back */
2733 if ( spi_result != SPI_OK_DELETE )
2734 {
2735 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2736 spi_result, sql->data);
2737 pfree(sqldata.data);
2738 return 0;
2739 }
2740 if ( SPI_processed ) topo->be_data->data_changed = true;
2741 }
2742 else
2743 {
2744 initStringInfo(sql);
2745 /* delete edge1 */
2746 appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
2747 "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 2"
2748 " AND l.topology_id = %d AND l.layer_id = r.layer_id "
2749 " AND abs(r.element_id) = %" LWTFMT_ELEMID,
2750 topo->name, topo->id, edge2 );
2751 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeHeal query 1: %s", sql->data);
2752
2753 spi_result = SPI_execute(sql->data, false, 0);
2754 MemoryContextSwitchTo( oldcontext ); /* switch back */
2755 if ( spi_result != SPI_OK_DELETE )
2756 {
2757 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2758 spi_result, sql->data);
2759 pfree(sqldata.data);
2760 return 0;
2761 }
2762 if ( SPI_processed ) topo->be_data->data_changed = true;
2763
2764 initStringInfo(sql);
2765 /* update edge2 to newedge */
2766 appendStringInfo( sql, "UPDATE \"%s\".relation r "
2767 "SET element_id = %" LWTFMT_ELEMID " *(element_id/%" LWTFMT_ELEMID
2768 ") FROM topology.layer l "
2769 "WHERE l.level = 0 AND l.feature_type = 2 AND l.topology_id = %d"
2770 " AND l.layer_id = r.layer_id AND abs(r.element_id) = %" LWTFMT_ELEMID,
2771 topo->name, newedge, edge1, topo->id, edge1 );
2772 POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeHeal query 2: %s", sql->data);
2773
2774 spi_result = SPI_execute(sql->data, false, 0);
2775 MemoryContextSwitchTo( oldcontext ); /* switch back */
2776 if ( spi_result != SPI_OK_UPDATE )
2777 {
2778 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2779 spi_result, sql->data);
2780 pfree(sqldata.data);
2781 return 0;
2782 }
2783 if ( SPI_processed ) topo->be_data->data_changed = true;
2784 }
2785
2786 return 1;
2787 }
2788
2789 static LWT_ELEMID
cb_getFaceContainingPoint(const LWT_BE_TOPOLOGY * topo,const LWPOINT * pt)2790 cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt )
2791 {
2792 MemoryContext oldcontext = CurrentMemoryContext;
2793 int spi_result;
2794 StringInfoData sqldata;
2795 StringInfo sql = &sqldata;
2796 bool isnull;
2797 Datum dat;
2798 LWT_ELEMID face_id;
2799 GSERIALIZED *pts;
2800 Datum values[1];
2801 Oid argtypes[1];
2802
2803 initStringInfo(sql);
2804
2805 pts = geometry_serialize(lwpoint_as_lwgeom(pt));
2806 if ( ! pts )
2807 {
2808 cberror(topo->be_data, "%s:%d: could not serialize query point",
2809 __FILE__, __LINE__);
2810 return -2;
2811 }
2812 /* TODO: call GetFaceGeometry internally, avoiding the round-trip to sql */
2813 appendStringInfo(sql,
2814 "WITH faces AS ( SELECT face_id FROM \"%s\".face "
2815 "WHERE mbr && $1 ORDER BY ST_Area(mbr) ASC ) "
2816 "SELECT face_id FROM faces WHERE _ST_Contains("
2817 "topology.ST_GetFaceGeometry('%s', face_id), $1)"
2818 " LIMIT 1",
2819 topo->name, topo->name);
2820
2821 values[0] = PointerGetDatum(pts);
2822 argtypes[0] = topo->geometryOID;
2823 spi_result = SPI_execute_with_args(sql->data, 1, argtypes, values, NULL,
2824 !topo->be_data->data_changed, 1);
2825 MemoryContextSwitchTo( oldcontext ); /* switch back */
2826 pfree(pts); /* not needed anymore */
2827 if ( spi_result != SPI_OK_SELECT )
2828 {
2829 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2830 spi_result, sql->data);
2831 pfree(sqldata.data);
2832 return -2;
2833 }
2834 pfree(sqldata.data);
2835
2836 if ( SPI_processed != 1 )
2837 {
2838 return -1; /* none found */
2839 }
2840
2841 dat = SPI_getbinval( SPI_tuptable->vals[0],
2842 SPI_tuptable->tupdesc, 1, &isnull );
2843 if ( isnull )
2844 {
2845 SPI_freetuptable(SPI_tuptable);
2846 cberror(topo->be_data, "corrupted topology: face with NULL face_id");
2847 return -2;
2848 }
2849 face_id = DatumGetInt32(dat);
2850 SPI_freetuptable(SPI_tuptable);
2851 return face_id;
2852 }
2853
2854 static int
cb_deleteFacesById(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int numelems)2855 cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo,
2856 const LWT_ELEMID* ids, int numelems )
2857 {
2858 MemoryContext oldcontext = CurrentMemoryContext;
2859 int spi_result, i;
2860 StringInfoData sqldata;
2861 StringInfo sql = &sqldata;
2862
2863 initStringInfo(sql);
2864 appendStringInfo(sql, "DELETE FROM \"%s\".face WHERE face_id IN (", topo->name);
2865 for (i=0; i<numelems; ++i)
2866 {
2867 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
2868 }
2869 appendStringInfoString(sql, ")");
2870
2871 POSTGIS_DEBUGF(1, "cb_deleteFacesById query: %s", sql->data);
2872
2873 spi_result = SPI_execute( sql->data, false, 0 );
2874 MemoryContextSwitchTo( oldcontext ); /* switch back */
2875 if ( spi_result != SPI_OK_DELETE )
2876 {
2877 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2878 spi_result, sql->data);
2879 pfree(sqldata.data);
2880 return -1;
2881 }
2882 pfree(sqldata.data);
2883
2884 if ( SPI_processed ) topo->be_data->data_changed = true;
2885
2886 POSTGIS_DEBUGF(1, "cb_deleteFacesById: delete query processed %d rows",
2887 SPI_processed);
2888
2889 return SPI_processed;
2890 }
2891
2892 static int
cb_deleteNodesById(const LWT_BE_TOPOLOGY * topo,const LWT_ELEMID * ids,int numelems)2893 cb_deleteNodesById( const LWT_BE_TOPOLOGY* topo,
2894 const LWT_ELEMID* ids, int numelems )
2895 {
2896 MemoryContext oldcontext = CurrentMemoryContext;
2897 int spi_result, i;
2898 StringInfoData sqldata;
2899 StringInfo sql = &sqldata;
2900
2901 initStringInfo(sql);
2902 appendStringInfo(sql, "DELETE FROM \"%s\".node WHERE node_id IN (",
2903 topo->name);
2904 for (i=0; i<numelems; ++i)
2905 {
2906 appendStringInfo(sql, "%s%" LWTFMT_ELEMID, (i?",":""), ids[i]);
2907 }
2908 appendStringInfoString(sql, ")");
2909
2910 POSTGIS_DEBUGF(1, "cb_deleteNodesById query: %s", sql->data);
2911
2912 spi_result = SPI_execute( sql->data, false, 0 );
2913 MemoryContextSwitchTo( oldcontext ); /* switch back */
2914 if ( spi_result != SPI_OK_DELETE )
2915 {
2916 cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
2917 spi_result, sql->data);
2918 pfree(sqldata.data);
2919 return -1;
2920 }
2921 pfree(sqldata.data);
2922
2923 if ( SPI_processed ) topo->be_data->data_changed = true;
2924
2925 POSTGIS_DEBUGF(1, "cb_deleteNodesById: delete query processed %d rows",
2926 SPI_processed);
2927
2928 return SPI_processed;
2929 }
2930
2931 static LWT_ISO_NODE*
cb_getNodeWithinBox2D(const LWT_BE_TOPOLOGY * topo,const GBOX * box,int * numelems,int fields,int limit)2932 cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
2933 int* numelems, int fields, int limit )
2934 {
2935 MemoryContext oldcontext = CurrentMemoryContext;
2936 int spi_result;
2937 StringInfoData sqldata;
2938 StringInfo sql = &sqldata;
2939 int i;
2940 int elems_requested = limit;
2941 LWT_ISO_NODE* nodes;
2942 char *hexbox;
2943
2944 initStringInfo(sql);
2945
2946 if ( elems_requested == -1 )
2947 {
2948 appendStringInfoString(sql, "SELECT EXISTS ( SELECT 1");
2949 }
2950 else
2951 {
2952 appendStringInfoString(sql, "SELECT ");
2953 addNodeFields(sql, fields);
2954 }
2955 hexbox = _box2d_to_hexwkb(box, topo->srid);
2956 appendStringInfo(sql, " FROM \"%s\".node WHERE geom && '%s'::geometry",
2957 topo->name, hexbox);
2958 lwfree(hexbox);
2959 if ( elems_requested == -1 )
2960 {
2961 appendStringInfoString(sql, ")");
2962 }
2963 else if ( elems_requested > 0 )
2964 {
2965 appendStringInfo(sql, " LIMIT %d", elems_requested);
2966 }
2967 POSTGIS_DEBUGF(1,"cb_getNodeWithinBox2D: query is: %s", sql->data);
2968 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 0);
2969 MemoryContextSwitchTo( oldcontext ); /* switch back */
2970 if ( spi_result != SPI_OK_SELECT )
2971 {
2972 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
2973 pfree(sqldata.data);
2974 *numelems = -1;
2975 return NULL;
2976 }
2977 pfree(sqldata.data);
2978
2979 POSTGIS_DEBUGF(1, "cb_getNodeWithinBox2D: edge query "
2980 "(limited by %d) returned %d rows",
2981 elems_requested, SPI_processed);
2982 *numelems = SPI_processed;
2983 if ( ! SPI_processed )
2984 {
2985 return NULL;
2986 }
2987
2988 if ( elems_requested == -1 )
2989 {
2990 /* This was an EXISTS query */
2991 {
2992 Datum dat;
2993 bool isnull, exists;
2994 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
2995 exists = DatumGetBool(dat);
2996 SPI_freetuptable(SPI_tuptable);
2997 *numelems = exists ? 1 : 0;
2998 POSTGIS_DEBUGF(1, "cb_getNodeWithinBox2D: exists ? %d", *numelems);
2999 }
3000 return NULL;
3001 }
3002
3003 nodes = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
3004 for ( i=0; i<*numelems; ++i )
3005 {
3006 HeapTuple row = SPI_tuptable->vals[i];
3007 fillNodeFields(&nodes[i], row, SPI_tuptable->tupdesc, fields);
3008 }
3009
3010 SPI_freetuptable(SPI_tuptable);
3011
3012 return nodes;
3013 }
3014
3015 static LWT_ISO_EDGE*
cb_getEdgeWithinBox2D(const LWT_BE_TOPOLOGY * topo,const GBOX * box,int * numelems,int fields,int limit)3016 cb_getEdgeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
3017 int* numelems, int fields, int limit )
3018 {
3019 MemoryContext oldcontext = CurrentMemoryContext;
3020 int spi_result;
3021 StringInfoData sqldata;
3022 StringInfo sql = &sqldata;
3023 int i;
3024 int elems_requested = limit;
3025 LWT_ISO_EDGE* edges;
3026 char *hexbox;
3027
3028 initStringInfo(sql);
3029
3030 if ( elems_requested == -1 )
3031 {
3032 appendStringInfoString(sql, "SELECT EXISTS ( SELECT 1");
3033 }
3034 else
3035 {
3036 appendStringInfoString(sql, "SELECT ");
3037 addEdgeFields(sql, fields, 0);
3038 }
3039 appendStringInfo(sql, " FROM \"%s\".edge", topo->name);
3040
3041 if ( box )
3042 {
3043 hexbox = _box2d_to_hexwkb(box, topo->srid);
3044 appendStringInfo(sql, " WHERE geom && '%s'::geometry", hexbox);
3045 lwfree(hexbox);
3046 }
3047
3048 if ( elems_requested == -1 )
3049 {
3050 appendStringInfoString(sql, ")");
3051 }
3052 else if ( elems_requested > 0 )
3053 {
3054 appendStringInfo(sql, " LIMIT %d", elems_requested);
3055 }
3056 POSTGIS_DEBUGF(1,"cb_getEdgeWithinBox2D: query is: %s", sql->data);
3057 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 0);
3058 MemoryContextSwitchTo( oldcontext ); /* switch back */
3059 if ( spi_result != SPI_OK_SELECT )
3060 {
3061 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
3062 pfree(sqldata.data);
3063 *numelems = -1;
3064 return NULL;
3065 }
3066 pfree(sqldata.data);
3067
3068 POSTGIS_DEBUGF(1, "cb_getEdgeWithinBox2D: edge query "
3069 "(limited by %d) returned %d rows",
3070 elems_requested, SPI_processed);
3071 *numelems = SPI_processed;
3072 if ( ! SPI_processed )
3073 {
3074 return NULL;
3075 }
3076
3077 if ( elems_requested == -1 )
3078 {
3079 /* This was an EXISTS query */
3080 {
3081 Datum dat;
3082 bool isnull, exists;
3083 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
3084 exists = DatumGetBool(dat);
3085 *numelems = exists ? 1 : 0;
3086 SPI_freetuptable(SPI_tuptable);
3087 POSTGIS_DEBUGF(1, "cb_getEdgeWithinBox2D: exists ? %d", *numelems);
3088 }
3089 return NULL;
3090 }
3091
3092 edges = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
3093 for ( i=0; i<*numelems; ++i )
3094 {
3095 HeapTuple row = SPI_tuptable->vals[i];
3096 fillEdgeFields(&edges[i], row, SPI_tuptable->tupdesc, fields);
3097 }
3098
3099 SPI_freetuptable(SPI_tuptable);
3100
3101 return edges;
3102 }
3103
3104 static LWT_ISO_FACE*
cb_getFaceWithinBox2D(const LWT_BE_TOPOLOGY * topo,const GBOX * box,int * numelems,int fields,int limit)3105 cb_getFaceWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
3106 int* numelems, int fields, int limit )
3107 {
3108 MemoryContext oldcontext = CurrentMemoryContext;
3109 int spi_result;
3110 StringInfoData sqldata;
3111 StringInfo sql = &sqldata;
3112 int i;
3113 int elems_requested = limit;
3114 LWT_ISO_FACE* faces;
3115 char *hexbox;
3116
3117 initStringInfo(sql);
3118
3119 if ( elems_requested == -1 )
3120 {
3121 appendStringInfoString(sql, "SELECT EXISTS ( SELECT 1");
3122 }
3123 else
3124 {
3125 appendStringInfoString(sql, "SELECT ");
3126 addFaceFields(sql, fields);
3127 }
3128 hexbox = _box2d_to_hexwkb(box, topo->srid);
3129 appendStringInfo(sql, " FROM \"%s\".face WHERE mbr && '%s'::geometry",
3130 topo->name, hexbox);
3131 lwfree(hexbox);
3132 if ( elems_requested == -1 )
3133 {
3134 appendStringInfoString(sql, ")");
3135 }
3136 else if ( elems_requested > 0 )
3137 {
3138 appendStringInfo(sql, " LIMIT %d", elems_requested);
3139 }
3140 POSTGIS_DEBUGF(1,"cb_getFaceWithinBox2D: query is: %s", sql->data);
3141 spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 0);
3142 MemoryContextSwitchTo( oldcontext ); /* switch back */
3143 if ( spi_result != SPI_OK_SELECT )
3144 {
3145 cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
3146 pfree(sqldata.data);
3147 *numelems = -1;
3148 return NULL;
3149 }
3150 pfree(sqldata.data);
3151
3152 POSTGIS_DEBUGF(1, "cb_getFaceWithinBox2D: face query "
3153 "(limited by %d) returned %d rows",
3154 elems_requested, SPI_processed);
3155 *numelems = SPI_processed;
3156 if ( ! SPI_processed )
3157 {
3158 return NULL;
3159 }
3160
3161 if ( elems_requested == -1 )
3162 {
3163 /* This was an EXISTS query */
3164 {
3165 Datum dat;
3166 bool isnull, exists;
3167 dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
3168 exists = DatumGetBool(dat);
3169 *numelems = exists ? 1 : 0;
3170 POSTGIS_DEBUGF(1, "cb_getFaceWithinBox2D: exists ? %d", *numelems);
3171 }
3172
3173 SPI_freetuptable(SPI_tuptable);
3174
3175 return NULL;
3176 }
3177
3178 faces = palloc( sizeof(LWT_ISO_EDGE) * *numelems );
3179 for ( i=0; i<*numelems; ++i )
3180 {
3181 HeapTuple row = SPI_tuptable->vals[i];
3182 fillFaceFields(&faces[i], row, SPI_tuptable->tupdesc, fields);
3183 }
3184
3185 SPI_freetuptable(SPI_tuptable);
3186
3187 return faces;
3188 }
3189
3190
3191 static LWT_BE_CALLBACKS be_callbacks =
3192 {
3193 cb_lastErrorMessage,
3194 NULL, /* createTopology */
3195 cb_loadTopologyByName,
3196 cb_freeTopology,
3197 cb_getNodeById,
3198 cb_getNodeWithinDistance2D,
3199 cb_insertNodes,
3200 cb_getEdgeById,
3201 cb_getEdgeWithinDistance2D,
3202 cb_getNextEdgeId,
3203 cb_insertEdges,
3204 cb_updateEdges,
3205 cb_getFacesById,
3206 cb_getFaceContainingPoint,
3207 cb_updateTopoGeomEdgeSplit,
3208 cb_deleteEdges,
3209 cb_getNodeWithinBox2D,
3210 cb_getEdgeWithinBox2D,
3211 cb_getEdgeByNode,
3212 cb_updateNodes,
3213 cb_updateTopoGeomFaceSplit,
3214 cb_insertFaces,
3215 cb_updateFacesById,
3216 cb_getRingEdges,
3217 cb_updateEdgesById,
3218 cb_getEdgeByFace,
3219 cb_getNodeByFace,
3220 cb_updateNodesById,
3221 cb_deleteFacesById,
3222 cb_topoGetSRID,
3223 cb_topoGetPrecision,
3224 cb_topoHasZ,
3225 cb_deleteNodesById,
3226 cb_checkTopoGeomRemEdge,
3227 cb_updateTopoGeomFaceHeal,
3228 cb_checkTopoGeomRemNode,
3229 cb_updateTopoGeomEdgeHeal,
3230 cb_getFaceWithinBox2D
3231 };
3232
3233 static void
xact_callback(XactEvent event,void * arg)3234 xact_callback(XactEvent event, void *arg)
3235 {
3236 LWT_BE_DATA* data = (LWT_BE_DATA *)arg;
3237 POSTGIS_DEBUGF(1, "xact_callback called with event %d", event);
3238 data->data_changed = false;
3239 }
3240
3241
3242 /*
3243 * Module load callback
3244 */
3245 void _PG_init(void);
3246 void
_PG_init(void)3247 _PG_init(void)
3248 {
3249 MemoryContext old_context;
3250
3251 /*
3252 * install PostgreSQL handlers for liblwgeom
3253 * NOTE: they may be already in place!
3254 */
3255 pg_install_lwgeom_handlers();
3256
3257 /* Switch to the top memory context so that the backend interface
3258 * is valid for the whole backend lifetime */
3259 old_context = MemoryContextSwitchTo( TopMemoryContext );
3260
3261 /* initialize backend data */
3262 be_data.data_changed = false;
3263 be_data.topoLoadFailMessageFlavor = 0;
3264
3265 /* hook on transaction end to reset data_changed */
3266 RegisterXactCallback(xact_callback, &be_data);
3267
3268 /* register callbacks against liblwgeom-topo */
3269 be_iface = lwt_CreateBackendIface(&be_data);
3270 lwt_BackendIfaceRegisterCallbacks(be_iface, &be_callbacks);
3271
3272 /* Switch back to whatever memory context was in place
3273 * at time of _PG_init enter.
3274 * See http://www.postgresql.org/message-id/20150623114125.GD5835@localhost
3275 */
3276 MemoryContextSwitchTo(old_context);
3277 }
3278
3279 /*
3280 * Module unload callback
3281 */
3282 void _PG_fini(void);
3283 void
_PG_fini(void)3284 _PG_fini(void)
3285 {
3286 elog(NOTICE, "Goodbye from PostGIS Topology %s", POSTGIS_VERSION);
3287
3288 UnregisterXactCallback(xact_callback, &be_data);
3289 lwt_FreeBackendIface(be_iface);
3290 }
3291
3292 /* ST_ModEdgeSplit(atopology, anedge, apoint) */
3293 Datum ST_ModEdgeSplit(PG_FUNCTION_ARGS);
3294 PG_FUNCTION_INFO_V1(ST_ModEdgeSplit);
ST_ModEdgeSplit(PG_FUNCTION_ARGS)3295 Datum ST_ModEdgeSplit(PG_FUNCTION_ARGS)
3296 {
3297 text* toponame_text;
3298 char* toponame;
3299 LWT_ELEMID edge_id;
3300 LWT_ELEMID node_id;
3301 GSERIALIZED *geom;
3302 LWGEOM *lwgeom;
3303 LWPOINT *pt;
3304 LWT_TOPOLOGY *topo;
3305
3306 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
3307 {
3308 lwpgerror("SQL/MM Spatial exception - null argument");
3309 PG_RETURN_NULL();
3310 }
3311
3312 toponame_text = PG_GETARG_TEXT_P(0);
3313 toponame = text_to_cstring(toponame_text);
3314 PG_FREE_IF_COPY(toponame_text, 0);
3315
3316 edge_id = PG_GETARG_INT32(1) ;
3317
3318 geom = PG_GETARG_GSERIALIZED_P(2);
3319 lwgeom = lwgeom_from_gserialized(geom);
3320 pt = lwgeom_as_lwpoint(lwgeom);
3321 if ( ! pt )
3322 {
3323 lwgeom_free(lwgeom);
3324 PG_FREE_IF_COPY(geom, 2);
3325 lwpgerror("ST_ModEdgeSplit third argument must be a point geometry");
3326 PG_RETURN_NULL();
3327 }
3328
3329 if ( SPI_OK_CONNECT != SPI_connect() )
3330 {
3331 lwpgerror("Could not connect to SPI");
3332 PG_RETURN_NULL();
3333 }
3334
3335 topo = lwt_LoadTopology(be_iface, toponame);
3336 pfree(toponame);
3337 if ( ! topo )
3338 {
3339 /* should never reach this point, as lwerror would raise an exception */
3340 SPI_finish();
3341 PG_RETURN_NULL();
3342 }
3343
3344 POSTGIS_DEBUG(1, "Calling lwt_ModEdgeSplit");
3345 node_id = lwt_ModEdgeSplit(topo, edge_id, pt, 0);
3346 POSTGIS_DEBUG(1, "lwt_ModEdgeSplit returned");
3347 lwgeom_free(lwgeom);
3348 PG_FREE_IF_COPY(geom, 3);
3349 lwt_FreeTopology(topo);
3350
3351 if ( node_id == -1 )
3352 {
3353 /* should never reach this point, as lwerror would raise an exception */
3354 SPI_finish();
3355 PG_RETURN_NULL();
3356 }
3357
3358 SPI_finish();
3359 PG_RETURN_INT32(node_id);
3360 }
3361
3362 /* ST_NewEdgesSplit(atopology, anedge, apoint) */
3363 Datum ST_NewEdgesSplit(PG_FUNCTION_ARGS);
3364 PG_FUNCTION_INFO_V1(ST_NewEdgesSplit);
ST_NewEdgesSplit(PG_FUNCTION_ARGS)3365 Datum ST_NewEdgesSplit(PG_FUNCTION_ARGS)
3366 {
3367 text* toponame_text;
3368 char* toponame;
3369 LWT_ELEMID edge_id;
3370 LWT_ELEMID node_id;
3371 GSERIALIZED *geom;
3372 LWGEOM *lwgeom;
3373 LWPOINT *pt;
3374 LWT_TOPOLOGY *topo;
3375
3376 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
3377 {
3378 lwpgerror("SQL/MM Spatial exception - null argument");
3379 PG_RETURN_NULL();
3380 }
3381
3382 toponame_text = PG_GETARG_TEXT_P(0);
3383 toponame = text_to_cstring(toponame_text);
3384 PG_FREE_IF_COPY(toponame_text, 0);
3385
3386 edge_id = PG_GETARG_INT32(1) ;
3387
3388 geom = PG_GETARG_GSERIALIZED_P(2);
3389 lwgeom = lwgeom_from_gserialized(geom);
3390 pt = lwgeom_as_lwpoint(lwgeom);
3391 if ( ! pt )
3392 {
3393 lwgeom_free(lwgeom);
3394 PG_FREE_IF_COPY(geom, 2);
3395 lwpgerror("ST_NewEdgesSplit third argument must be a point geometry");
3396 PG_RETURN_NULL();
3397 }
3398
3399 if ( SPI_OK_CONNECT != SPI_connect() )
3400 {
3401 lwpgerror("Could not connect to SPI");
3402 PG_RETURN_NULL();
3403 }
3404
3405 topo = lwt_LoadTopology(be_iface, toponame);
3406 pfree(toponame);
3407 if ( ! topo )
3408 {
3409 /* should never reach this point, as lwerror would raise an exception */
3410 SPI_finish();
3411 PG_RETURN_NULL();
3412 }
3413
3414 POSTGIS_DEBUG(1, "Calling lwt_NewEdgesSplit");
3415 node_id = lwt_NewEdgesSplit(topo, edge_id, pt, 0);
3416 POSTGIS_DEBUG(1, "lwt_NewEdgesSplit returned");
3417 lwgeom_free(lwgeom);
3418 PG_FREE_IF_COPY(geom, 3);
3419 lwt_FreeTopology(topo);
3420
3421 if ( node_id == -1 )
3422 {
3423 /* should never reach this point, as lwerror would raise an exception */
3424 SPI_finish();
3425 PG_RETURN_NULL();
3426 }
3427
3428 SPI_finish();
3429 PG_RETURN_INT32(node_id);
3430 }
3431
3432 /* ST_AddIsoNode(atopology, aface, apoint) */
3433 Datum ST_AddIsoNode(PG_FUNCTION_ARGS);
3434 PG_FUNCTION_INFO_V1(ST_AddIsoNode);
ST_AddIsoNode(PG_FUNCTION_ARGS)3435 Datum ST_AddIsoNode(PG_FUNCTION_ARGS)
3436 {
3437 text* toponame_text;
3438 char* toponame;
3439 LWT_ELEMID containing_face;
3440 LWT_ELEMID node_id;
3441 GSERIALIZED *geom;
3442 LWGEOM *lwgeom;
3443 LWPOINT *pt;
3444 LWT_TOPOLOGY *topo;
3445
3446 if ( PG_ARGISNULL(0) || PG_ARGISNULL(2) )
3447 {
3448 lwpgerror("SQL/MM Spatial exception - null argument");
3449 PG_RETURN_NULL();
3450 }
3451
3452 toponame_text = PG_GETARG_TEXT_P(0);
3453 toponame = text_to_cstring(toponame_text);
3454 PG_FREE_IF_COPY(toponame_text, 0);
3455
3456 if ( PG_ARGISNULL(1) ) containing_face = -1;
3457 else
3458 {
3459 containing_face = PG_GETARG_INT32(1);
3460 if ( containing_face < 0 )
3461 {
3462 lwpgerror("SQL/MM Spatial exception - not within face");
3463 PG_RETURN_NULL();
3464 }
3465 }
3466
3467 geom = PG_GETARG_GSERIALIZED_P(2);
3468 lwgeom = lwgeom_from_gserialized(geom);
3469 pt = lwgeom_as_lwpoint(lwgeom);
3470 if ( ! pt )
3471 {
3472 lwgeom_free(lwgeom);
3473 PG_FREE_IF_COPY(geom, 2);
3474 lwpgerror("SQL/MM Spatial exception - invalid point");
3475 PG_RETURN_NULL();
3476 }
3477 if ( lwpoint_is_empty(pt) )
3478 {
3479 lwgeom_free(lwgeom);
3480 PG_FREE_IF_COPY(geom, 2);
3481 lwpgerror("SQL/MM Spatial exception - empty point");
3482 PG_RETURN_NULL();
3483 }
3484
3485 if ( SPI_OK_CONNECT != SPI_connect() )
3486 {
3487 lwpgerror("Could not connect to SPI");
3488 PG_RETURN_NULL();
3489 }
3490
3491 topo = lwt_LoadTopology(be_iface, toponame);
3492 pfree(toponame);
3493 if ( ! topo )
3494 {
3495 /* should never reach this point, as lwerror would raise an exception */
3496 SPI_finish();
3497 PG_RETURN_NULL();
3498 }
3499
3500 POSTGIS_DEBUG(1, "Calling lwt_AddIsoNode");
3501 node_id = lwt_AddIsoNode(topo, containing_face, pt, 0);
3502 POSTGIS_DEBUG(1, "lwt_AddIsoNode returned");
3503 lwgeom_free(lwgeom);
3504 PG_FREE_IF_COPY(geom, 2);
3505 lwt_FreeTopology(topo);
3506
3507 if ( node_id == -1 )
3508 {
3509 /* should never reach this point, as lwerror would raise an exception */
3510 SPI_finish();
3511 PG_RETURN_NULL();
3512 }
3513
3514 SPI_finish();
3515 PG_RETURN_INT32(node_id);
3516 }
3517
3518 /* ST_AddIsoEdge(atopology, anode, anothernode, acurve) */
3519 Datum ST_AddIsoEdge(PG_FUNCTION_ARGS);
3520 PG_FUNCTION_INFO_V1(ST_AddIsoEdge);
ST_AddIsoEdge(PG_FUNCTION_ARGS)3521 Datum ST_AddIsoEdge(PG_FUNCTION_ARGS)
3522 {
3523 text* toponame_text;
3524 char* toponame;
3525 LWT_ELEMID edge_id;
3526 LWT_ELEMID start_node, end_node;
3527 GSERIALIZED *geom;
3528 LWGEOM *lwgeom;
3529 LWLINE *curve;
3530 LWT_TOPOLOGY *topo;
3531
3532 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ||
3533 PG_ARGISNULL(2) || PG_ARGISNULL(3) )
3534 {
3535 lwpgerror("SQL/MM Spatial exception - null argument");
3536 PG_RETURN_NULL();
3537 }
3538
3539 toponame_text = PG_GETARG_TEXT_P(0);
3540 toponame = text_to_cstring(toponame_text);
3541 PG_FREE_IF_COPY(toponame_text, 0);
3542
3543 start_node = PG_GETARG_INT32(1);
3544 end_node = PG_GETARG_INT32(2);
3545
3546 if ( start_node == end_node )
3547 {
3548 lwpgerror("Closed edges would not be isolated, try ST_AddEdgeNewFaces");
3549 PG_RETURN_NULL();
3550 }
3551
3552 geom = PG_GETARG_GSERIALIZED_P(3);
3553 lwgeom = lwgeom_from_gserialized(geom);
3554 curve = lwgeom_as_lwline(lwgeom);
3555 if ( ! curve )
3556 {
3557 lwgeom_free(lwgeom);
3558 PG_FREE_IF_COPY(geom, 3);
3559 lwpgerror("SQL/MM Spatial exception - invalid curve");
3560 PG_RETURN_NULL();
3561 }
3562
3563 if ( SPI_OK_CONNECT != SPI_connect() )
3564 {
3565 lwpgerror("Could not connect to SPI");
3566 PG_RETURN_NULL();
3567 }
3568
3569 topo = lwt_LoadTopology(be_iface, toponame);
3570 pfree(toponame);
3571 if ( ! topo )
3572 {
3573 /* should never reach this point, as lwerror would raise an exception */
3574 SPI_finish();
3575 PG_RETURN_NULL();
3576 }
3577
3578 POSTGIS_DEBUG(1, "Calling lwt_AddIsoEdge");
3579 edge_id = lwt_AddIsoEdge(topo, start_node, end_node, curve);
3580 POSTGIS_DEBUG(1, "lwt_AddIsoNode returned");
3581 lwgeom_free(lwgeom);
3582 PG_FREE_IF_COPY(geom, 3);
3583 lwt_FreeTopology(topo);
3584
3585 if ( edge_id == -1 )
3586 {
3587 /* should never reach this point, as lwerror would raise an exception */
3588 SPI_finish();
3589 PG_RETURN_NULL();
3590 }
3591
3592 SPI_finish();
3593 PG_RETURN_INT32(edge_id);
3594 }
3595
3596 /* ST_AddEdgeModFace(atopology, snode, enode, line) */
3597 Datum ST_AddEdgeModFace(PG_FUNCTION_ARGS);
3598 PG_FUNCTION_INFO_V1(ST_AddEdgeModFace);
ST_AddEdgeModFace(PG_FUNCTION_ARGS)3599 Datum ST_AddEdgeModFace(PG_FUNCTION_ARGS)
3600 {
3601 text* toponame_text;
3602 char* toponame;
3603 LWT_ELEMID startnode_id, endnode_id;
3604 int edge_id;
3605 GSERIALIZED *geom;
3606 LWGEOM *lwgeom;
3607 LWLINE *line;
3608 LWT_TOPOLOGY *topo;
3609
3610 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3) )
3611 {
3612 lwpgerror("SQL/MM Spatial exception - null argument");
3613 PG_RETURN_NULL();
3614 }
3615
3616 toponame_text = PG_GETARG_TEXT_P(0);
3617 toponame = text_to_cstring(toponame_text);
3618 PG_FREE_IF_COPY(toponame_text, 0);
3619
3620 startnode_id = PG_GETARG_INT32(1) ;
3621 endnode_id = PG_GETARG_INT32(2) ;
3622
3623 geom = PG_GETARG_GSERIALIZED_P(3);
3624 lwgeom = lwgeom_from_gserialized(geom);
3625 line = lwgeom_as_lwline(lwgeom);
3626 if ( ! line )
3627 {
3628 lwgeom_free(lwgeom);
3629 PG_FREE_IF_COPY(geom, 3);
3630 lwpgerror("ST_AddEdgeModFace fourth argument must be a line geometry");
3631 PG_RETURN_NULL();
3632 }
3633
3634 if ( SPI_OK_CONNECT != SPI_connect() )
3635 {
3636 lwpgerror("Could not connect to SPI");
3637 PG_RETURN_NULL();
3638 }
3639
3640 topo = lwt_LoadTopology(be_iface, toponame);
3641 pfree(toponame);
3642 if ( ! topo )
3643 {
3644 /* should never reach this point, as lwerror would raise an exception */
3645 SPI_finish();
3646 PG_RETURN_NULL();
3647 }
3648
3649 POSTGIS_DEBUG(1, "Calling lwt_AddEdgeModFace");
3650 edge_id = lwt_AddEdgeModFace(topo, startnode_id, endnode_id, line, 0);
3651 POSTGIS_DEBUG(1, "lwt_AddEdgeModFace returned");
3652 lwgeom_free(lwgeom);
3653 PG_FREE_IF_COPY(geom, 3);
3654 lwt_FreeTopology(topo);
3655
3656 if ( edge_id == -1 )
3657 {
3658 /* should never reach this point, as lwerror would raise an exception */
3659 SPI_finish();
3660 PG_RETURN_NULL();
3661 }
3662
3663 SPI_finish();
3664 PG_RETURN_INT32(edge_id);
3665 }
3666
3667 /* ST_AddEdgeNewFaces(atopology, snode, enode, line) */
3668 Datum ST_AddEdgeNewFaces(PG_FUNCTION_ARGS);
3669 PG_FUNCTION_INFO_V1(ST_AddEdgeNewFaces);
ST_AddEdgeNewFaces(PG_FUNCTION_ARGS)3670 Datum ST_AddEdgeNewFaces(PG_FUNCTION_ARGS)
3671 {
3672 text* toponame_text;
3673 char* toponame;
3674 LWT_ELEMID startnode_id, endnode_id;
3675 int edge_id;
3676 GSERIALIZED *geom;
3677 LWGEOM *lwgeom;
3678 LWLINE *line;
3679 LWT_TOPOLOGY *topo;
3680
3681 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3) )
3682 {
3683 lwpgerror("SQL/MM Spatial exception - null argument");
3684 PG_RETURN_NULL();
3685 }
3686
3687 toponame_text = PG_GETARG_TEXT_P(0);
3688 toponame = text_to_cstring(toponame_text);
3689 PG_FREE_IF_COPY(toponame_text, 0);
3690
3691 startnode_id = PG_GETARG_INT32(1) ;
3692 endnode_id = PG_GETARG_INT32(2) ;
3693
3694 geom = PG_GETARG_GSERIALIZED_P(3);
3695 lwgeom = lwgeom_from_gserialized(geom);
3696 line = lwgeom_as_lwline(lwgeom);
3697 if ( ! line )
3698 {
3699 lwgeom_free(lwgeom);
3700 PG_FREE_IF_COPY(geom, 3);
3701 lwpgerror("ST_AddEdgeModFace fourth argument must be a line geometry");
3702 PG_RETURN_NULL();
3703 }
3704
3705 if ( SPI_OK_CONNECT != SPI_connect() )
3706 {
3707 lwpgerror("Could not connect to SPI");
3708 PG_RETURN_NULL();
3709 }
3710
3711 topo = lwt_LoadTopology(be_iface, toponame);
3712 pfree(toponame);
3713 if ( ! topo )
3714 {
3715 /* should never reach this point, as lwerror would raise an exception */
3716 SPI_finish();
3717 PG_RETURN_NULL();
3718 }
3719
3720 POSTGIS_DEBUG(1, "Calling lwt_AddEdgeNewFaces");
3721 edge_id = lwt_AddEdgeNewFaces(topo, startnode_id, endnode_id, line, 0);
3722 POSTGIS_DEBUG(1, "lwt_AddEdgeNewFaces returned");
3723 lwgeom_free(lwgeom);
3724 PG_FREE_IF_COPY(geom, 3);
3725 lwt_FreeTopology(topo);
3726
3727 if ( edge_id == -1 )
3728 {
3729 /* should never reach this point, as lwerror would raise an exception */
3730 SPI_finish();
3731 PG_RETURN_NULL();
3732 }
3733
3734 SPI_finish();
3735 PG_RETURN_INT32(edge_id);
3736 }
3737
3738 /* ST_GetFaceGeometry(atopology, aface) */
3739 Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS);
3740 PG_FUNCTION_INFO_V1(ST_GetFaceGeometry);
ST_GetFaceGeometry(PG_FUNCTION_ARGS)3741 Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS)
3742 {
3743 text* toponame_text;
3744 char* toponame;
3745 LWT_ELEMID face_id;
3746 LWGEOM *lwgeom;
3747 LWT_TOPOLOGY *topo;
3748 GSERIALIZED *geom;
3749 MemoryContext old_context;
3750
3751 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
3752 {
3753 lwpgerror("SQL/MM Spatial exception - null argument");
3754 PG_RETURN_NULL();
3755 }
3756
3757 toponame_text = PG_GETARG_TEXT_P(0);
3758 toponame = text_to_cstring(toponame_text);
3759 PG_FREE_IF_COPY(toponame_text, 0);
3760
3761 face_id = PG_GETARG_INT32(1) ;
3762
3763 if ( SPI_OK_CONNECT != SPI_connect() )
3764 {
3765 lwpgerror("Could not connect to SPI");
3766 PG_RETURN_NULL();
3767 }
3768
3769 topo = lwt_LoadTopology(be_iface, toponame);
3770 pfree(toponame);
3771 if ( ! topo )
3772 {
3773 /* should never reach this point, as lwerror would raise an exception */
3774 SPI_finish();
3775 PG_RETURN_NULL();
3776 }
3777
3778 POSTGIS_DEBUG(1, "Calling lwt_GetFaceGeometry");
3779 lwgeom = lwt_GetFaceGeometry(topo, face_id);
3780 POSTGIS_DEBUG(1, "lwt_GetFaceGeometry returned");
3781 lwt_FreeTopology(topo);
3782
3783 if ( lwgeom == NULL )
3784 {
3785 /* should never reach this point, as lwerror would raise an exception */
3786 SPI_finish();
3787 PG_RETURN_NULL();
3788 }
3789
3790 /* Serialize in upper memory context (outside of SPI) */
3791 /* TODO: use a narrower context to switch to */
3792 old_context = MemoryContextSwitchTo( TopMemoryContext );
3793 geom = geometry_serialize(lwgeom);
3794 MemoryContextSwitchTo(old_context);
3795
3796 SPI_finish();
3797
3798 PG_RETURN_POINTER(geom);
3799 }
3800
3801 typedef struct FACEEDGESSTATE
3802 {
3803 LWT_ELEMID *elems;
3804 int nelems;
3805 int curr;
3806 }
3807 FACEEDGESSTATE;
3808
3809 /* ST_GetFaceEdges(atopology, aface) */
3810 Datum ST_GetFaceEdges(PG_FUNCTION_ARGS);
3811 PG_FUNCTION_INFO_V1(ST_GetFaceEdges);
ST_GetFaceEdges(PG_FUNCTION_ARGS)3812 Datum ST_GetFaceEdges(PG_FUNCTION_ARGS)
3813 {
3814 text* toponame_text;
3815 char* toponame;
3816 LWT_ELEMID face_id;
3817 int nelems;
3818 LWT_ELEMID *elems;
3819 LWT_TOPOLOGY *topo;
3820 FuncCallContext *funcctx;
3821 MemoryContext oldcontext, newcontext;
3822 TupleDesc tupdesc;
3823 HeapTuple tuple;
3824 AttInMetadata *attinmeta;
3825 FACEEDGESSTATE *state;
3826 char buf[64];
3827 char *values[2];
3828 Datum result;
3829
3830 values[0] = buf;
3831 values[1] = &(buf[32]);
3832
3833 if (SRF_IS_FIRSTCALL())
3834 {
3835
3836 POSTGIS_DEBUG(1, "ST_GetFaceEdges first call");
3837 funcctx = SRF_FIRSTCALL_INIT();
3838 newcontext = funcctx->multi_call_memory_ctx;
3839
3840 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
3841 {
3842 lwpgerror("SQL/MM Spatial exception - null argument");
3843 PG_RETURN_NULL();
3844 }
3845
3846 toponame_text = PG_GETARG_TEXT_P(0);
3847 toponame = text_to_cstring(toponame_text);
3848 PG_FREE_IF_COPY(toponame_text, 0);
3849
3850 face_id = PG_GETARG_INT32(1) ;
3851
3852 if ( SPI_OK_CONNECT != SPI_connect() )
3853 {
3854 lwpgerror("Could not connect to SPI");
3855 PG_RETURN_NULL();
3856 }
3857
3858 topo = lwt_LoadTopology(be_iface, toponame);
3859 oldcontext = MemoryContextSwitchTo( newcontext );
3860 pfree(toponame);
3861 if ( ! topo )
3862 {
3863 /* should never reach this point, as lwerror would raise an exception */
3864 SPI_finish();
3865 PG_RETURN_NULL();
3866 }
3867
3868 POSTGIS_DEBUG(1, "Calling lwt_GetFaceEdges");
3869 nelems = lwt_GetFaceEdges(topo, face_id, &elems);
3870 POSTGIS_DEBUGF(1, "lwt_GetFaceEdges returned %d", nelems);
3871 lwt_FreeTopology(topo);
3872
3873 if ( nelems < 0 )
3874 {
3875 /* should never reach this point, as lwerror would raise an exception */
3876 SPI_finish();
3877 PG_RETURN_NULL();
3878 }
3879
3880 state = lwalloc(sizeof(FACEEDGESSTATE));
3881 state->elems = elems;
3882 state->nelems = nelems;
3883 state->curr = 0;
3884 funcctx->user_fctx = state;
3885
3886 /*
3887 * Build a tuple description for a
3888 * getfaceedges_returntype tuple
3889 */
3890 tupdesc = RelationNameGetTupleDesc("topology.getfaceedges_returntype");
3891
3892 /*
3893 * generate attribute metadata needed later to produce
3894 * tuples from raw C strings
3895 */
3896 attinmeta = TupleDescGetAttInMetadata(tupdesc);
3897 funcctx->attinmeta = attinmeta;
3898
3899 POSTGIS_DEBUG(1, "lwt_GetFaceEdges calling SPI_finish");
3900
3901 MemoryContextSwitchTo(oldcontext);
3902
3903 SPI_finish();
3904 }
3905
3906 /* stuff done on every call of the function */
3907 funcctx = SRF_PERCALL_SETUP();
3908
3909 /* get state */
3910 state = funcctx->user_fctx;
3911
3912 if ( state->curr == state->nelems )
3913 {
3914 SRF_RETURN_DONE(funcctx);
3915 }
3916
3917 if ( snprintf(values[0], 32, "%d", state->curr+1) >= 32 )
3918 {
3919 lwerror("Face edge sequence number does not fit 32 chars ?!: %d",
3920 state->curr+1);
3921 }
3922 if ( snprintf(values[1], 32, "%" LWTFMT_ELEMID,
3923 state->elems[state->curr]) >= 32 )
3924 {
3925 lwerror("Signed edge identifier does not fit 32 chars ?!: %"
3926 LWTFMT_ELEMID, state->elems[state->curr]);
3927 }
3928
3929 POSTGIS_DEBUGF(1, "ST_GetFaceEdges: cur:%d, val0:%s, val1:%s",
3930 state->curr, values[0], values[1]);
3931
3932 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
3933 result = HeapTupleGetDatum(tuple);
3934 state->curr++;
3935
3936 SRF_RETURN_NEXT(funcctx, result);
3937 }
3938
3939 /* ST_ChangeEdgeGeom(atopology, anedge, acurve) */
3940 Datum ST_ChangeEdgeGeom(PG_FUNCTION_ARGS);
3941 PG_FUNCTION_INFO_V1(ST_ChangeEdgeGeom);
ST_ChangeEdgeGeom(PG_FUNCTION_ARGS)3942 Datum ST_ChangeEdgeGeom(PG_FUNCTION_ARGS)
3943 {
3944 text* toponame_text;
3945 char buf[64];
3946 char* toponame;
3947 int ret;
3948 LWT_ELEMID edge_id;
3949 GSERIALIZED *geom;
3950 LWGEOM *lwgeom;
3951 LWLINE *line;
3952 LWT_TOPOLOGY *topo;
3953
3954 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
3955 {
3956 lwpgerror("SQL/MM Spatial exception - null argument");
3957 PG_RETURN_NULL();
3958 }
3959
3960 toponame_text = PG_GETARG_TEXT_P(0);
3961 toponame = text_to_cstring(toponame_text);
3962 PG_FREE_IF_COPY(toponame_text, 0);
3963
3964 edge_id = PG_GETARG_INT32(1) ;
3965
3966 geom = PG_GETARG_GSERIALIZED_P(2);
3967 lwgeom = lwgeom_from_gserialized(geom);
3968 line = lwgeom_as_lwline(lwgeom);
3969 if ( ! line )
3970 {
3971 lwgeom_free(lwgeom);
3972 PG_FREE_IF_COPY(geom, 2);
3973 lwpgerror("ST_ChangeEdgeGeom third argument must be a line geometry");
3974 PG_RETURN_NULL();
3975 }
3976
3977 if ( SPI_OK_CONNECT != SPI_connect() )
3978 {
3979 lwpgerror("Could not connect to SPI");
3980 PG_RETURN_NULL();
3981 }
3982
3983 topo = lwt_LoadTopology(be_iface, toponame);
3984 pfree(toponame);
3985 if ( ! topo )
3986 {
3987 /* should never reach this point, as lwerror would raise an exception */
3988 SPI_finish();
3989 PG_RETURN_NULL();
3990 }
3991
3992 POSTGIS_DEBUG(1, "Calling lwt_ChangeEdgeGeom");
3993 ret = lwt_ChangeEdgeGeom(topo, edge_id, line);
3994 POSTGIS_DEBUG(1, "lwt_ChangeEdgeGeom returned");
3995 lwgeom_free(lwgeom);
3996 PG_FREE_IF_COPY(geom, 2);
3997 lwt_FreeTopology(topo);
3998
3999 if ( ret == -1 )
4000 {
4001 /* should never reach this point, as lwerror would raise an exception */
4002 SPI_finish();
4003 PG_RETURN_NULL();
4004 }
4005
4006 SPI_finish();
4007
4008 if ( snprintf(buf, 64, "Edge %" LWTFMT_ELEMID " changed", edge_id) >= 64 )
4009 {
4010 buf[63] = '\0';
4011 }
4012 PG_RETURN_TEXT_P(cstring_to_text(buf));
4013 }
4014
4015 /* ST_RemoveIsoNode(atopology, anode) */
4016 Datum ST_RemoveIsoNode(PG_FUNCTION_ARGS);
4017 PG_FUNCTION_INFO_V1(ST_RemoveIsoNode);
ST_RemoveIsoNode(PG_FUNCTION_ARGS)4018 Datum ST_RemoveIsoNode(PG_FUNCTION_ARGS)
4019 {
4020 text* toponame_text;
4021 char buf[64];
4022 char* toponame;
4023 int ret;
4024 LWT_ELEMID node_id;
4025 LWT_TOPOLOGY *topo;
4026
4027 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
4028 {
4029 lwpgerror("SQL/MM Spatial exception - null argument");
4030 PG_RETURN_NULL();
4031 }
4032
4033 toponame_text = PG_GETARG_TEXT_P(0);
4034 toponame = text_to_cstring(toponame_text);
4035 PG_FREE_IF_COPY(toponame_text, 0);
4036
4037 node_id = PG_GETARG_INT32(1) ;
4038
4039 if ( SPI_OK_CONNECT != SPI_connect() )
4040 {
4041 lwpgerror("Could not connect to SPI");
4042 PG_RETURN_NULL();
4043 }
4044
4045 topo = lwt_LoadTopology(be_iface, toponame);
4046 pfree(toponame);
4047 if ( ! topo )
4048 {
4049 /* should never reach this point, as lwerror would raise an exception */
4050 SPI_finish();
4051 PG_RETURN_NULL();
4052 }
4053
4054 POSTGIS_DEBUG(1, "Calling lwt_RemoveIsoNode");
4055 ret = lwt_RemoveIsoNode(topo, node_id);
4056 POSTGIS_DEBUG(1, "lwt_RemoveIsoNode returned");
4057 lwt_FreeTopology(topo);
4058
4059 if ( ret == -1 )
4060 {
4061 /* should never reach this point, as lwerror would raise an exception */
4062 SPI_finish();
4063 PG_RETURN_NULL();
4064 }
4065
4066 /* TODO: check if any TopoGeometry exists including this point in
4067 * its definition ! */
4068
4069 SPI_finish();
4070
4071 if ( snprintf(buf, 64, "Isolated node %" LWTFMT_ELEMID
4072 " removed", node_id) >= 64 )
4073 {
4074 buf[63] = '\0';
4075 }
4076 PG_RETURN_TEXT_P(cstring_to_text(buf));
4077 }
4078
4079 /* ST_RemIsoEdge(atopology, anedge) */
4080 Datum ST_RemIsoEdge(PG_FUNCTION_ARGS);
4081 PG_FUNCTION_INFO_V1(ST_RemIsoEdge);
ST_RemIsoEdge(PG_FUNCTION_ARGS)4082 Datum ST_RemIsoEdge(PG_FUNCTION_ARGS)
4083 {
4084 text* toponame_text;
4085 char buf[64];
4086 char* toponame;
4087 int ret;
4088 LWT_ELEMID node_id;
4089 LWT_TOPOLOGY *topo;
4090
4091 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
4092 {
4093 lwpgerror("SQL/MM Spatial exception - null argument");
4094 PG_RETURN_NULL();
4095 }
4096
4097 toponame_text = PG_GETARG_TEXT_P(0);
4098 toponame = text_to_cstring(toponame_text);
4099 PG_FREE_IF_COPY(toponame_text, 0);
4100
4101 node_id = PG_GETARG_INT32(1) ;
4102
4103 if ( SPI_OK_CONNECT != SPI_connect() )
4104 {
4105 lwpgerror("Could not connect to SPI");
4106 PG_RETURN_NULL();
4107 }
4108
4109 topo = lwt_LoadTopology(be_iface, toponame);
4110 pfree(toponame);
4111 if ( ! topo )
4112 {
4113 /* should never reach this point, as lwerror would raise an exception */
4114 SPI_finish();
4115 PG_RETURN_NULL();
4116 }
4117
4118 POSTGIS_DEBUG(1, "Calling lwt_RemIsoEdge");
4119 ret = lwt_RemIsoEdge(topo, node_id);
4120 POSTGIS_DEBUG(1, "lwt_RemIsoEdge returned");
4121 lwt_FreeTopology(topo);
4122
4123 if ( ret == -1 )
4124 {
4125 /* should never reach this point, as lwerror would raise an exception */
4126 SPI_finish();
4127 PG_RETURN_NULL();
4128 }
4129
4130 /* TODO: check if any TopoGeometry exists including this point in
4131 * its definition ! */
4132
4133 SPI_finish();
4134
4135 if ( snprintf(buf, 64, "Isolated edge %" LWTFMT_ELEMID
4136 " removed", node_id) >= 64 )
4137 {
4138 buf[63] = '\0';
4139 }
4140 PG_RETURN_TEXT_P(cstring_to_text(buf));
4141 }
4142
4143 /* ST_MoveIsoNode(atopology, anode, apoint) */
4144 Datum ST_MoveIsoNode(PG_FUNCTION_ARGS);
4145 PG_FUNCTION_INFO_V1(ST_MoveIsoNode);
ST_MoveIsoNode(PG_FUNCTION_ARGS)4146 Datum ST_MoveIsoNode(PG_FUNCTION_ARGS)
4147 {
4148 text* toponame_text;
4149 char buf[64];
4150 char* toponame;
4151 int ret;
4152 LWT_ELEMID node_id;
4153 GSERIALIZED *geom;
4154 LWGEOM *lwgeom;
4155 LWPOINT *pt;
4156 LWT_TOPOLOGY *topo;
4157 POINT2D p;
4158
4159 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4160 {
4161 lwpgerror("SQL/MM Spatial exception - null argument");
4162 PG_RETURN_NULL();
4163 }
4164
4165 toponame_text = PG_GETARG_TEXT_P(0);
4166 toponame = text_to_cstring(toponame_text);
4167 PG_FREE_IF_COPY(toponame_text, 0);
4168
4169 node_id = PG_GETARG_INT32(1) ;
4170
4171 geom = PG_GETARG_GSERIALIZED_P(2);
4172 lwgeom = lwgeom_from_gserialized(geom);
4173 pt = lwgeom_as_lwpoint(lwgeom);
4174 if ( ! pt )
4175 {
4176 lwgeom_free(lwgeom);
4177 PG_FREE_IF_COPY(geom, 2);
4178 lwpgerror("SQL/MM Spatial exception - invalid point");
4179 PG_RETURN_NULL();
4180 }
4181
4182 if ( ! getPoint2d_p(pt->point, 0, &p) )
4183 {
4184 /* Do not let empty points in, see
4185 * https://trac.osgeo.org/postgis/ticket/3234
4186 */
4187 lwpgerror("SQL/MM Spatial exception - empty point");
4188 PG_RETURN_NULL();
4189 }
4190
4191 /* TODO: check point for NaN values ? */
4192
4193 if ( SPI_OK_CONNECT != SPI_connect() )
4194 {
4195 lwpgerror("Could not connect to SPI");
4196 PG_RETURN_NULL();
4197 }
4198
4199 topo = lwt_LoadTopology(be_iface, toponame);
4200 pfree(toponame);
4201 if ( ! topo )
4202 {
4203 /* should never reach this point, as lwerror would raise an exception */
4204 SPI_finish();
4205 PG_RETURN_NULL();
4206 }
4207
4208 POSTGIS_DEBUG(1, "Calling lwt_MoveIsoNode");
4209 ret = lwt_MoveIsoNode(topo, node_id, pt);
4210 POSTGIS_DEBUG(1, "lwt_MoveIsoNode returned");
4211 lwgeom_free(lwgeom);
4212 PG_FREE_IF_COPY(geom, 2);
4213 lwt_FreeTopology(topo);
4214
4215 if ( ret == -1 )
4216 {
4217 /* should never reach this point, as lwerror would raise an exception */
4218 SPI_finish();
4219 PG_RETURN_NULL();
4220 }
4221
4222 SPI_finish();
4223
4224 if ( snprintf(buf, 64, "Isolated Node %" LWTFMT_ELEMID
4225 " moved to location %g,%g",
4226 node_id, p.x, p.y) >= 64 )
4227 {
4228 buf[63] = '\0';
4229 }
4230 PG_RETURN_TEXT_P(cstring_to_text(buf));
4231 }
4232
4233 /* ST_RemEdgeModFace(atopology, anedge) */
4234 Datum ST_RemEdgeModFace(PG_FUNCTION_ARGS);
4235 PG_FUNCTION_INFO_V1(ST_RemEdgeModFace);
ST_RemEdgeModFace(PG_FUNCTION_ARGS)4236 Datum ST_RemEdgeModFace(PG_FUNCTION_ARGS)
4237 {
4238 text* toponame_text;
4239 char* toponame;
4240 int ret;
4241 LWT_ELEMID node_id;
4242 LWT_TOPOLOGY *topo;
4243
4244 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
4245 {
4246 lwpgerror("SQL/MM Spatial exception - null argument");
4247 PG_RETURN_NULL();
4248 }
4249
4250 toponame_text = PG_GETARG_TEXT_P(0);
4251 toponame = text_to_cstring(toponame_text);
4252 PG_FREE_IF_COPY(toponame_text, 0);
4253
4254 node_id = PG_GETARG_INT32(1) ;
4255
4256 if ( SPI_OK_CONNECT != SPI_connect() )
4257 {
4258 lwpgerror("Could not connect to SPI");
4259 PG_RETURN_NULL();
4260 }
4261
4262 topo = lwt_LoadTopology(be_iface, toponame);
4263 pfree(toponame);
4264 if ( ! topo )
4265 {
4266 /* should never reach this point, as lwerror would raise an exception */
4267 SPI_finish();
4268 PG_RETURN_NULL();
4269 }
4270
4271 POSTGIS_DEBUG(1, "Calling lwt_RemEdgeModFace");
4272 ret = lwt_RemEdgeModFace(topo, node_id);
4273 POSTGIS_DEBUG(1, "lwt_RemEdgeModFace returned");
4274 lwt_FreeTopology(topo);
4275
4276 if ( ret == -1 )
4277 {
4278 /* should never reach this point, as lwerror would raise an exception */
4279 SPI_finish();
4280 PG_RETURN_NULL();
4281 }
4282
4283 SPI_finish();
4284
4285 PG_RETURN_INT32(ret);
4286 }
4287
4288 /* ST_RemEdgeNewFace(atopology, anedge) */
4289 Datum ST_RemEdgeNewFace(PG_FUNCTION_ARGS);
4290 PG_FUNCTION_INFO_V1(ST_RemEdgeNewFace);
ST_RemEdgeNewFace(PG_FUNCTION_ARGS)4291 Datum ST_RemEdgeNewFace(PG_FUNCTION_ARGS)
4292 {
4293 text* toponame_text;
4294 char* toponame;
4295 int ret;
4296 LWT_ELEMID node_id;
4297 LWT_TOPOLOGY *topo;
4298
4299 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
4300 {
4301 lwpgerror("SQL/MM Spatial exception - null argument");
4302 PG_RETURN_NULL();
4303 }
4304
4305 toponame_text = PG_GETARG_TEXT_P(0);
4306 toponame = text_to_cstring(toponame_text);
4307 PG_FREE_IF_COPY(toponame_text, 0);
4308
4309 node_id = PG_GETARG_INT32(1) ;
4310
4311 if ( SPI_OK_CONNECT != SPI_connect() )
4312 {
4313 lwpgerror("Could not connect to SPI");
4314 PG_RETURN_NULL();
4315 }
4316
4317 topo = lwt_LoadTopology(be_iface, toponame);
4318 pfree(toponame);
4319 if ( ! topo )
4320 {
4321 /* should never reach this point, as lwerror would raise an exception */
4322 SPI_finish();
4323 PG_RETURN_NULL();
4324 }
4325
4326 POSTGIS_DEBUG(1, "Calling lwt_RemEdgeNewFace");
4327 ret = lwt_RemEdgeNewFace(topo, node_id);
4328 POSTGIS_DEBUG(1, "lwt_RemEdgeNewFace returned");
4329 lwt_FreeTopology(topo);
4330 SPI_finish();
4331
4332 if ( ret <= 0 )
4333 {
4334 /* error or no face created */
4335 PG_RETURN_NULL();
4336 }
4337
4338 PG_RETURN_INT32(ret);
4339 }
4340
4341 /* ST_ModEdgeHeal(atopology, anedge, anotheredge) */
4342 Datum ST_ModEdgeHeal(PG_FUNCTION_ARGS);
4343 PG_FUNCTION_INFO_V1(ST_ModEdgeHeal);
ST_ModEdgeHeal(PG_FUNCTION_ARGS)4344 Datum ST_ModEdgeHeal(PG_FUNCTION_ARGS)
4345 {
4346 text* toponame_text;
4347 char* toponame;
4348 int ret;
4349 LWT_ELEMID eid1, eid2;
4350 LWT_TOPOLOGY *topo;
4351
4352 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4353 {
4354 lwpgerror("SQL/MM Spatial exception - null argument");
4355 PG_RETURN_NULL();
4356 }
4357
4358 toponame_text = PG_GETARG_TEXT_P(0);
4359 toponame = text_to_cstring(toponame_text);
4360 PG_FREE_IF_COPY(toponame_text, 0);
4361
4362 eid1 = PG_GETARG_INT32(1) ;
4363 eid2 = PG_GETARG_INT32(2) ;
4364
4365 if ( SPI_OK_CONNECT != SPI_connect() )
4366 {
4367 lwpgerror("Could not connect to SPI");
4368 PG_RETURN_NULL();
4369 }
4370
4371 topo = lwt_LoadTopology(be_iface, toponame);
4372 pfree(toponame);
4373 if ( ! topo )
4374 {
4375 /* should never reach this point, as lwerror would raise an exception */
4376 SPI_finish();
4377 PG_RETURN_NULL();
4378 }
4379
4380 POSTGIS_DEBUG(1, "Calling lwt_ModEdgeHeal");
4381 ret = lwt_ModEdgeHeal(topo, eid1, eid2);
4382 POSTGIS_DEBUG(1, "lwt_ModEdgeHeal returned");
4383 lwt_FreeTopology(topo);
4384 SPI_finish();
4385
4386 if ( ret <= 0 )
4387 {
4388 /* error, should have sent message already */
4389 PG_RETURN_NULL();
4390 }
4391
4392 PG_RETURN_INT32(ret);
4393 }
4394
4395 /* ST_NewEdgeHeal(atopology, anedge, anotheredge) */
4396 Datum ST_NewEdgeHeal(PG_FUNCTION_ARGS);
4397 PG_FUNCTION_INFO_V1(ST_NewEdgeHeal);
ST_NewEdgeHeal(PG_FUNCTION_ARGS)4398 Datum ST_NewEdgeHeal(PG_FUNCTION_ARGS)
4399 {
4400 text* toponame_text;
4401 char* toponame;
4402 int ret;
4403 LWT_ELEMID eid1, eid2;
4404 LWT_TOPOLOGY *topo;
4405
4406 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4407 {
4408 lwpgerror("SQL/MM Spatial exception - null argument");
4409 PG_RETURN_NULL();
4410 }
4411
4412 toponame_text = PG_GETARG_TEXT_P(0);
4413 toponame = text_to_cstring(toponame_text);
4414 PG_FREE_IF_COPY(toponame_text, 0);
4415
4416 eid1 = PG_GETARG_INT32(1) ;
4417 eid2 = PG_GETARG_INT32(2) ;
4418
4419 if ( SPI_OK_CONNECT != SPI_connect() )
4420 {
4421 lwpgerror("Could not connect to SPI");
4422 PG_RETURN_NULL();
4423 }
4424
4425 topo = lwt_LoadTopology(be_iface, toponame);
4426 pfree(toponame);
4427 if ( ! topo )
4428 {
4429 /* should never reach this point, as lwerror would raise an exception */
4430 SPI_finish();
4431 PG_RETURN_NULL();
4432 }
4433
4434 POSTGIS_DEBUG(1, "Calling lwt_NewEdgeHeal");
4435 ret = lwt_NewEdgeHeal(topo, eid1, eid2);
4436 POSTGIS_DEBUG(1, "lwt_NewEdgeHeal returned");
4437 lwt_FreeTopology(topo);
4438 SPI_finish();
4439
4440 if ( ret <= 0 )
4441 {
4442 /* error, should have sent message already */
4443 PG_RETURN_NULL();
4444 }
4445
4446 PG_RETURN_INT32(ret);
4447 }
4448
4449 /* GetNodeByPoint(atopology, point, tolerance) */
4450 Datum GetNodeByPoint(PG_FUNCTION_ARGS);
4451 PG_FUNCTION_INFO_V1(GetNodeByPoint);
GetNodeByPoint(PG_FUNCTION_ARGS)4452 Datum GetNodeByPoint(PG_FUNCTION_ARGS)
4453 {
4454 text* toponame_text;
4455 char* toponame;
4456 double tol;
4457 LWT_ELEMID node_id;
4458 GSERIALIZED *geom;
4459 LWGEOM *lwgeom;
4460 LWPOINT *pt;
4461 LWT_TOPOLOGY *topo;
4462
4463 toponame_text = PG_GETARG_TEXT_P(0);
4464 toponame = text_to_cstring(toponame_text);
4465 PG_FREE_IF_COPY(toponame_text, 0);
4466
4467 geom = PG_GETARG_GSERIALIZED_P(1);
4468 lwgeom = lwgeom_from_gserialized(geom);
4469 pt = lwgeom_as_lwpoint(lwgeom);
4470 if ( ! pt )
4471 {
4472 lwgeom_free(lwgeom);
4473 PG_FREE_IF_COPY(geom, 1);
4474 lwpgerror("Node geometry must be a point");
4475 PG_RETURN_NULL();
4476 }
4477
4478 tol = PG_GETARG_FLOAT8(2);
4479 if ( tol < 0 )
4480 {
4481 PG_FREE_IF_COPY(geom, 1);
4482 lwpgerror("Tolerance must be >=0");
4483 PG_RETURN_NULL();
4484 }
4485
4486 if ( SPI_OK_CONNECT != SPI_connect() )
4487 {
4488 lwpgerror("Could not connect to SPI");
4489 PG_RETURN_NULL();
4490 }
4491
4492 topo = lwt_LoadTopology(be_iface, toponame);
4493 pfree(toponame);
4494 if ( ! topo )
4495 {
4496 /* should never reach this point, as lwerror would raise an exception */
4497 SPI_finish();
4498 PG_RETURN_NULL();
4499 }
4500
4501 POSTGIS_DEBUG(1, "Calling lwt_GetNodeByPoint");
4502 node_id = lwt_GetNodeByPoint(topo, pt, tol);
4503 POSTGIS_DEBUG(1, "lwt_GetNodeByPoint returned");
4504 lwgeom_free(lwgeom);
4505 PG_FREE_IF_COPY(geom, 1);
4506 lwt_FreeTopology(topo);
4507
4508 if ( node_id == -1 )
4509 {
4510 /* should never reach this point, as lwerror would raise an exception */
4511 SPI_finish();
4512 PG_RETURN_NULL();
4513 }
4514
4515 SPI_finish();
4516 PG_RETURN_INT32(node_id);
4517 }
4518
4519 /* GetEdgeByPoint(atopology, point, tolerance) */
4520 Datum GetEdgeByPoint(PG_FUNCTION_ARGS);
4521 PG_FUNCTION_INFO_V1(GetEdgeByPoint);
GetEdgeByPoint(PG_FUNCTION_ARGS)4522 Datum GetEdgeByPoint(PG_FUNCTION_ARGS)
4523 {
4524 text* toponame_text;
4525 char* toponame;
4526 double tol;
4527 LWT_ELEMID node_id;
4528 GSERIALIZED *geom;
4529 LWGEOM *lwgeom;
4530 LWPOINT *pt;
4531 LWT_TOPOLOGY *topo;
4532
4533 toponame_text = PG_GETARG_TEXT_P(0);
4534 toponame = text_to_cstring(toponame_text);
4535 PG_FREE_IF_COPY(toponame_text, 0);
4536
4537 geom = PG_GETARG_GSERIALIZED_P(1);
4538 lwgeom = lwgeom_from_gserialized(geom);
4539 pt = lwgeom_as_lwpoint(lwgeom);
4540 if ( ! pt )
4541 {
4542 lwgeom_free(lwgeom);
4543 PG_FREE_IF_COPY(geom, 1);
4544 lwpgerror("Node geometry must be a point");
4545 PG_RETURN_NULL();
4546 }
4547
4548 tol = PG_GETARG_FLOAT8(2);
4549 if ( tol < 0 )
4550 {
4551 PG_FREE_IF_COPY(geom, 1);
4552 lwpgerror("Tolerance must be >=0");
4553 PG_RETURN_NULL();
4554 }
4555
4556 if ( SPI_OK_CONNECT != SPI_connect() )
4557 {
4558 lwpgerror("Could not connect to SPI");
4559 PG_RETURN_NULL();
4560 }
4561
4562 topo = lwt_LoadTopology(be_iface, toponame);
4563 pfree(toponame);
4564 if ( ! topo )
4565 {
4566 /* should never reach this point, as lwerror would raise an exception */
4567 SPI_finish();
4568 PG_RETURN_NULL();
4569 }
4570
4571 POSTGIS_DEBUG(1, "Calling lwt_GetEdgeByPoint");
4572 node_id = lwt_GetEdgeByPoint(topo, pt, tol);
4573 POSTGIS_DEBUG(1, "lwt_GetEdgeByPoint returned");
4574 lwgeom_free(lwgeom);
4575 PG_FREE_IF_COPY(geom, 1);
4576 lwt_FreeTopology(topo);
4577
4578 if ( node_id == -1 )
4579 {
4580 /* should never reach this point, as lwerror would raise an exception */
4581 SPI_finish();
4582 PG_RETURN_NULL();
4583 }
4584
4585 SPI_finish();
4586 PG_RETURN_INT32(node_id);
4587 }
4588
4589 /* GetFaceByPoint(atopology, point, tolerance) */
4590 Datum GetFaceByPoint(PG_FUNCTION_ARGS);
4591 PG_FUNCTION_INFO_V1(GetFaceByPoint);
GetFaceByPoint(PG_FUNCTION_ARGS)4592 Datum GetFaceByPoint(PG_FUNCTION_ARGS)
4593 {
4594 text* toponame_text;
4595 char* toponame;
4596 double tol;
4597 LWT_ELEMID node_id;
4598 GSERIALIZED *geom;
4599 LWGEOM *lwgeom;
4600 LWPOINT *pt;
4601 LWT_TOPOLOGY *topo;
4602
4603 toponame_text = PG_GETARG_TEXT_P(0);
4604 toponame = text_to_cstring(toponame_text);
4605 PG_FREE_IF_COPY(toponame_text, 0);
4606
4607 geom = PG_GETARG_GSERIALIZED_P(1);
4608 lwgeom = lwgeom_from_gserialized(geom);
4609 pt = lwgeom_as_lwpoint(lwgeom);
4610 if ( ! pt )
4611 {
4612 lwgeom_free(lwgeom);
4613 PG_FREE_IF_COPY(geom, 1);
4614 lwpgerror("Node geometry must be a point");
4615 PG_RETURN_NULL();
4616 }
4617
4618 tol = PG_GETARG_FLOAT8(2);
4619 if ( tol < 0 )
4620 {
4621 PG_FREE_IF_COPY(geom, 1);
4622 lwpgerror("Tolerance must be >=0");
4623 PG_RETURN_NULL();
4624 }
4625
4626 if ( SPI_OK_CONNECT != SPI_connect() )
4627 {
4628 lwpgerror("Could not connect to SPI");
4629 PG_RETURN_NULL();
4630 }
4631
4632 topo = lwt_LoadTopology(be_iface, toponame);
4633 pfree(toponame);
4634 if ( ! topo )
4635 {
4636 /* should never reach this point, as lwerror would raise an exception */
4637 SPI_finish();
4638 PG_RETURN_NULL();
4639 }
4640
4641 POSTGIS_DEBUG(1, "Calling lwt_GetFaceByPoint");
4642 node_id = lwt_GetFaceByPoint(topo, pt, tol);
4643 POSTGIS_DEBUG(1, "lwt_GetFaceByPoint returned");
4644 lwgeom_free(lwgeom);
4645 PG_FREE_IF_COPY(geom, 1);
4646 lwt_FreeTopology(topo);
4647
4648 if ( node_id == -1 )
4649 {
4650 /* should never reach this point, as lwerror would raise an exception */
4651 SPI_finish();
4652 PG_RETURN_NULL();
4653 }
4654
4655 SPI_finish();
4656 PG_RETURN_INT32(node_id);
4657 }
4658
4659 /* TopoGeo_AddPoint(atopology, point, tolerance) */
4660 Datum TopoGeo_AddPoint(PG_FUNCTION_ARGS);
4661 PG_FUNCTION_INFO_V1(TopoGeo_AddPoint);
TopoGeo_AddPoint(PG_FUNCTION_ARGS)4662 Datum TopoGeo_AddPoint(PG_FUNCTION_ARGS)
4663 {
4664 text* toponame_text;
4665 char* toponame;
4666 double tol;
4667 LWT_ELEMID node_id;
4668 GSERIALIZED *geom;
4669 LWGEOM *lwgeom;
4670 LWPOINT *pt;
4671 LWT_TOPOLOGY *topo;
4672
4673 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4674 {
4675 lwpgerror("SQL/MM Spatial exception - null argument");
4676 PG_RETURN_NULL();
4677 }
4678
4679 toponame_text = PG_GETARG_TEXT_P(0);
4680 toponame = text_to_cstring(toponame_text);
4681 PG_FREE_IF_COPY(toponame_text, 0);
4682
4683 geom = PG_GETARG_GSERIALIZED_P(1);
4684 lwgeom = lwgeom_from_gserialized(geom);
4685 pt = lwgeom_as_lwpoint(lwgeom);
4686 if ( ! pt )
4687 {
4688 {
4689 char buf[32];
4690 _lwtype_upper_name(lwgeom_get_type(lwgeom), buf, 32);
4691 lwgeom_free(lwgeom);
4692 PG_FREE_IF_COPY(geom, 1);
4693 lwpgerror("Invalid geometry type (%s) passed to TopoGeo_AddPoint"
4694 ", expected POINT", buf );
4695 PG_RETURN_NULL();
4696 }
4697 }
4698
4699 tol = PG_GETARG_FLOAT8(2);
4700 if ( tol < 0 )
4701 {
4702 PG_FREE_IF_COPY(geom, 1);
4703 lwpgerror("Tolerance must be >=0");
4704 PG_RETURN_NULL();
4705 }
4706
4707 if ( SPI_OK_CONNECT != SPI_connect() )
4708 {
4709 lwpgerror("Could not connect to SPI");
4710 PG_RETURN_NULL();
4711 }
4712
4713 {
4714 int pre = be_data.topoLoadFailMessageFlavor;
4715 be_data.topoLoadFailMessageFlavor = 1;
4716 topo = lwt_LoadTopology(be_iface, toponame);
4717 be_data.topoLoadFailMessageFlavor = pre;
4718 }
4719 pfree(toponame);
4720 if ( ! topo )
4721 {
4722 /* should never reach this point, as lwerror would raise an exception */
4723 SPI_finish();
4724 PG_RETURN_NULL();
4725 }
4726
4727 POSTGIS_DEBUG(1, "Calling lwt_AddPoint");
4728 node_id = lwt_AddPoint(topo, pt, tol);
4729 POSTGIS_DEBUG(1, "lwt_AddPoint returned");
4730 lwgeom_free(lwgeom);
4731 PG_FREE_IF_COPY(geom, 1);
4732 lwt_FreeTopology(topo);
4733
4734 if ( node_id == -1 )
4735 {
4736 /* should never reach this point, as lwerror would raise an exception */
4737 SPI_finish();
4738 PG_RETURN_NULL();
4739 }
4740
4741 SPI_finish();
4742 PG_RETURN_INT32(node_id);
4743 }
4744
4745 /* TopoGeo_AddLinestring(atopology, point, tolerance) */
4746 Datum TopoGeo_AddLinestring(PG_FUNCTION_ARGS);
4747 PG_FUNCTION_INFO_V1(TopoGeo_AddLinestring);
TopoGeo_AddLinestring(PG_FUNCTION_ARGS)4748 Datum TopoGeo_AddLinestring(PG_FUNCTION_ARGS)
4749 {
4750 text* toponame_text;
4751 char* toponame;
4752 double tol;
4753 LWT_ELEMID *elems;
4754 int nelems;
4755 GSERIALIZED *geom;
4756 LWGEOM *lwgeom;
4757 LWLINE *ln;
4758 LWT_TOPOLOGY *topo;
4759 FuncCallContext *funcctx;
4760 MemoryContext oldcontext, newcontext;
4761 FACEEDGESSTATE *state;
4762 Datum result;
4763 LWT_ELEMID id;
4764
4765 if (SRF_IS_FIRSTCALL())
4766 {
4767 POSTGIS_DEBUG(1, "TopoGeo_AddLinestring first call");
4768 funcctx = SRF_FIRSTCALL_INIT();
4769 newcontext = funcctx->multi_call_memory_ctx;
4770
4771 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4772 {
4773 lwpgerror("SQL/MM Spatial exception - null argument");
4774 PG_RETURN_NULL();
4775 }
4776
4777 toponame_text = PG_GETARG_TEXT_P(0);
4778 toponame = text_to_cstring(toponame_text);
4779 PG_FREE_IF_COPY(toponame_text, 0);
4780
4781 geom = PG_GETARG_GSERIALIZED_P(1);
4782 lwgeom = lwgeom_from_gserialized(geom);
4783 ln = lwgeom_as_lwline(lwgeom);
4784 if ( ! ln )
4785 {
4786 {
4787 char buf[32];
4788 _lwtype_upper_name(lwgeom_get_type(lwgeom), buf, 32);
4789 lwgeom_free(lwgeom);
4790 PG_FREE_IF_COPY(geom, 1);
4791 lwpgerror("Invalid geometry type (%s) passed to "
4792 "TopoGeo_AddLinestring, expected LINESTRING", buf);
4793 PG_RETURN_NULL();
4794 }
4795 }
4796
4797 tol = PG_GETARG_FLOAT8(2);
4798 if ( tol < 0 )
4799 {
4800 PG_FREE_IF_COPY(geom, 1);
4801 lwpgerror("Tolerance must be >=0");
4802 PG_RETURN_NULL();
4803 }
4804
4805 if ( SPI_OK_CONNECT != SPI_connect() )
4806 {
4807 lwpgerror("Could not connect to SPI");
4808 PG_RETURN_NULL();
4809 }
4810
4811 {
4812 int pre = be_data.topoLoadFailMessageFlavor;
4813 be_data.topoLoadFailMessageFlavor = 1;
4814 topo = lwt_LoadTopology(be_iface, toponame);
4815 be_data.topoLoadFailMessageFlavor = pre;
4816 }
4817 oldcontext = MemoryContextSwitchTo( newcontext );
4818 pfree(toponame);
4819 if ( ! topo )
4820 {
4821 /* should never reach this point, as lwerror would raise an exception */
4822 SPI_finish();
4823 PG_RETURN_NULL();
4824 }
4825
4826 POSTGIS_DEBUG(1, "Calling lwt_AddLine");
4827 elems = lwt_AddLine(topo, ln, tol, &nelems);
4828 POSTGIS_DEBUG(1, "lwt_AddLine returned");
4829 lwgeom_free(lwgeom);
4830 PG_FREE_IF_COPY(geom, 1);
4831 lwt_FreeTopology(topo);
4832
4833 if ( nelems < 0 )
4834 {
4835 /* should never reach this point, as lwerror would raise an exception */
4836 SPI_finish();
4837 PG_RETURN_NULL();
4838 }
4839
4840 state = lwalloc(sizeof(FACEEDGESSTATE));
4841 state->elems = elems;
4842 state->nelems = nelems;
4843 state->curr = 0;
4844 funcctx->user_fctx = state;
4845
4846 POSTGIS_DEBUG(1, "TopoGeo_AddLinestring calling SPI_finish");
4847
4848 MemoryContextSwitchTo(oldcontext);
4849
4850 SPI_finish();
4851 }
4852
4853 POSTGIS_DEBUG(1, "Per-call invocation");
4854
4855 /* stuff done on every call of the function */
4856 funcctx = SRF_PERCALL_SETUP();
4857
4858 /* get state */
4859 state = funcctx->user_fctx;
4860
4861 if ( state->curr == state->nelems )
4862 {
4863 POSTGIS_DEBUG(1, "We're done, cleaning up all");
4864 SRF_RETURN_DONE(funcctx);
4865 }
4866
4867 id = state->elems[state->curr++];
4868 POSTGIS_DEBUGF(1, "TopoGeo_AddLinestring: cur:%d, val:%" LWTFMT_ELEMID,
4869 state->curr-1, id);
4870
4871 result = Int32GetDatum((int32)id);
4872
4873 SRF_RETURN_NEXT(funcctx, result);
4874 }
4875
4876 /* TopoGeo_AddPolygon(atopology, poly, tolerance) */
4877 Datum TopoGeo_AddPolygon(PG_FUNCTION_ARGS);
4878 PG_FUNCTION_INFO_V1(TopoGeo_AddPolygon);
TopoGeo_AddPolygon(PG_FUNCTION_ARGS)4879 Datum TopoGeo_AddPolygon(PG_FUNCTION_ARGS)
4880 {
4881 text* toponame_text;
4882 char* toponame;
4883 double tol;
4884 LWT_ELEMID *elems;
4885 int nelems;
4886 GSERIALIZED *geom;
4887 LWGEOM *lwgeom;
4888 LWPOLY *pol;
4889 LWT_TOPOLOGY *topo;
4890 FuncCallContext *funcctx;
4891 MemoryContext oldcontext, newcontext;
4892 FACEEDGESSTATE *state;
4893 Datum result;
4894 LWT_ELEMID id;
4895
4896 if (SRF_IS_FIRSTCALL())
4897 {
4898 POSTGIS_DEBUG(1, "TopoGeo_AddPolygon first call");
4899 funcctx = SRF_FIRSTCALL_INIT();
4900 newcontext = funcctx->multi_call_memory_ctx;
4901
4902 if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) )
4903 {
4904 lwpgerror("SQL/MM Spatial exception - null argument");
4905 PG_RETURN_NULL();
4906 }
4907
4908 toponame_text = PG_GETARG_TEXT_P(0);
4909 toponame = text_to_cstring(toponame_text);
4910 PG_FREE_IF_COPY(toponame_text, 0);
4911
4912 geom = PG_GETARG_GSERIALIZED_P(1);
4913 lwgeom = lwgeom_from_gserialized(geom);
4914 pol = lwgeom_as_lwpoly(lwgeom);
4915 if ( ! pol )
4916 {
4917 {
4918 char buf[32];
4919 _lwtype_upper_name(lwgeom_get_type(lwgeom), buf, 32);
4920 lwgeom_free(lwgeom);
4921 PG_FREE_IF_COPY(geom, 1);
4922 lwpgerror("Invalid geometry type (%s) passed to "
4923 "TopoGeo_AddPolygon, expected POLYGON", buf);
4924 PG_RETURN_NULL();
4925 }
4926 }
4927
4928 tol = PG_GETARG_FLOAT8(2);
4929 if ( tol < 0 )
4930 {
4931 PG_FREE_IF_COPY(geom, 1);
4932 lwpgerror("Tolerance must be >=0");
4933 PG_RETURN_NULL();
4934 }
4935
4936 if ( SPI_OK_CONNECT != SPI_connect() )
4937 {
4938 lwpgerror("Could not connect to SPI");
4939 PG_RETURN_NULL();
4940 }
4941
4942 {
4943 int pre = be_data.topoLoadFailMessageFlavor;
4944 be_data.topoLoadFailMessageFlavor = 1;
4945 topo = lwt_LoadTopology(be_iface, toponame);
4946 be_data.topoLoadFailMessageFlavor = pre;
4947 }
4948 oldcontext = MemoryContextSwitchTo( newcontext );
4949 pfree(toponame);
4950 if ( ! topo )
4951 {
4952 /* should never reach this point, as lwerror would raise an exception */
4953 SPI_finish();
4954 PG_RETURN_NULL();
4955 }
4956
4957 POSTGIS_DEBUG(1, "Calling lwt_AddPolygon");
4958 elems = lwt_AddPolygon(topo, pol, tol, &nelems);
4959 POSTGIS_DEBUG(1, "lwt_AddPolygon returned");
4960 lwgeom_free(lwgeom);
4961 PG_FREE_IF_COPY(geom, 1);
4962 lwt_FreeTopology(topo);
4963
4964 if ( nelems < 0 )
4965 {
4966 /* should never reach this point, as lwerror would raise an exception */
4967 SPI_finish();
4968 PG_RETURN_NULL();
4969 }
4970
4971 state = lwalloc(sizeof(FACEEDGESSTATE));
4972 state->elems = elems;
4973 state->nelems = nelems;
4974 state->curr = 0;
4975 funcctx->user_fctx = state;
4976
4977 POSTGIS_DEBUG(1, "TopoGeo_AddPolygon calling SPI_finish");
4978
4979 MemoryContextSwitchTo(oldcontext);
4980
4981 SPI_finish();
4982 }
4983
4984 POSTGIS_DEBUG(1, "Per-call invocation");
4985
4986 /* stuff done on every call of the function */
4987 funcctx = SRF_PERCALL_SETUP();
4988
4989 /* get state */
4990 state = funcctx->user_fctx;
4991
4992 if ( state->curr == state->nelems )
4993 {
4994 POSTGIS_DEBUG(1, "We're done, cleaning up all");
4995 SRF_RETURN_DONE(funcctx);
4996 }
4997
4998 id = state->elems[state->curr++];
4999 POSTGIS_DEBUGF(1, "TopoGeo_AddPolygon: cur:%d, val:%" LWTFMT_ELEMID,
5000 state->curr-1, id);
5001
5002 result = Int32GetDatum((int32)id);
5003
5004 SRF_RETURN_NEXT(funcctx, result);
5005 }
5006