GRASS GIS 7 Programmer's Manual  7.9.dev(2020)-547ded6c0
write_pg.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/write_pg.c
3 
4  \brief Vector library - write vector feature (PostGIS format)
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  Write subroutine inspired by OGR PostgreSQL driver.
9 
10  \todo PostGIS version of V2__delete_area_cats_from_cidx_nat()
11  \todo function to delete corresponding entry in fidx
12  \todo PostGIS version of V2__add_area_cats_to_cidx_nat
13 
14  (C) 2012-2014 by Martin Landa, and the GRASS Development Team
15 
16  This program is free software under the GNU General Public License
17  (>=v2). Read the file COPYING that comes with GRASS for details.
18 
19  \author Martin Landa <landa.martin gmail.com>
20  */
21 
22 #include <string.h>
23 
24 #include <grass/vector.h>
25 #include <grass/glocale.h>
26 
27 #include "local_proto.h"
28 
29 #ifdef HAVE_POSTGRES
30 #include "pg_local_proto.h"
31 
32 #define WKBSRIDFLAG 0x20000000
33 
34 #define TOPOGEOM_COLUMN "topo"
35 
36 /*! Use SQL statements from PostGIS Topology extension (this options
37  is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
38 */
39 #define USE_TOPO_STMT 0
40 
41 static int create_table(struct Format_info_pg *);
42 static int check_schema(const struct Format_info_pg *);
43 static int create_topo_schema(struct Format_info_pg *, int);
44 static int create_pg_layer(struct Map_info *, int);
45 static char *get_sftype(SF_FeatureType);
46 static off_t write_line_sf(struct Map_info *, int,
47  const struct line_pnts **, int,
48  const struct line_cats *);
49 static off_t write_line_tp(struct Map_info *, int, int,
50  const struct line_pnts *,
51  const struct line_cats *);
52 static char *binary_to_hex(int, const unsigned char *);
53 static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
54 static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
55  int, int *);
56 static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int,
57  int, int *);
58 static char *line_to_wkb(struct Format_info_pg *, const struct line_pnts **,
59  int, int, int);
60 static int write_feature(struct Map_info *, int, int,
61  const struct line_pnts **, int, int);
62 static char *build_insert_stmt(const struct Format_info_pg *, const char *, int, int);
63 static int insert_topo_element(struct Map_info *, int, int, const char *);
64 static int type_to_topogeom(const struct Format_info_pg *);
65 static int update_next_edge(struct Map_info*, int, int);
66 static int delete_face(const struct Map_info *, int);
67 static int update_topo_edge(struct Map_info *, int);
68 static int update_topo_face(struct Map_info *, int);
69 static int add_line_to_topo_pg(struct Map_info *, off_t, int, const struct line_pnts *);
70 static int delete_line_from_topo_pg(struct Map_info *, int, int, const struct line_pnts *);
71 static int set_constraint_to_deferrable(struct Format_info_pg *, const char *, const char *,
72  const char *, const char *, const char *);
73 static dbDriver *open_db(struct Format_info_pg *);
74 #endif
75 
76 static struct line_pnts *Points;
77 
78 /*!
79  \brief Writes feature on level 1 (PostGIS interface)
80 
81  Notes for simple feature access:
82  - centroids are not supported in PostGIS, pseudotopo holds virtual
83  centroids
84  - boundaries are not supported in PostGIS, pseudotopo treats polygons
85  as boundaries
86 
87  Notes for PostGIS Topology access:
88  - centroids are stored as isolated nodes
89  - boundaries are stored as edges
90 
91  \param Map pointer to Map_info structure
92  \param type feature type (GV_POINT, GV_LINE, ...)
93  \param points pointer to line_pnts structure (feature geometry)
94  \param cats pointer to line_cats structure (feature categories)
95 
96  \return feature offset into file
97  \return -1 on error
98  */
99 off_t V1_write_line_pg(struct Map_info *Map, int type,
100  const struct line_pnts *points,
101  const struct line_cats *cats)
102 {
103 #ifdef HAVE_POSTGRES
104  struct Format_info_pg *pg_info;
105 
106  pg_info = &(Map->fInfo.pg);
107 
108  if (pg_info->feature_type == SF_GEOMETRY) {
109  /* create PostGIS table if doesn't exist */
110  if (create_pg_layer(Map, type) < 0)
111  return -1;
112  }
113 
114  if (!points)
115  return 0;
116 
117  if (!pg_info->toposchema_name) { /* simple features access */
118  return write_line_sf(Map, type, &points, 1, cats);
119  }
120 
121  /* PostGIS Topology access */
122  return write_line_tp(Map, type, FALSE, points, cats);
123 #else
124  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
125  return -1;
126 #endif
127 }
128 
129 /*!
130  \brief Writes feature on topological level (PostGIS interface)
131 
132  Calls V2_write_line_sfa() for simple features access.
133 
134  \param Map pointer to Map_info structure
135  \param type feature type (GV_POINT, GV_LINE, ...)
136  \param points pointer to line_pnts structure (feature geometry)
137  \param cats pointer to line_cats structure (feature categories)
138 
139  \return feature offset into file
140  \return -1 on error
141  */
142 off_t V2_write_line_pg(struct Map_info *Map, int type,
143  const struct line_pnts *points,
144  const struct line_cats *cats)
145 {
146 #ifdef HAVE_POSTGRES
147  struct Format_info_pg *pg_info;
148 
149  pg_info = &(Map->fInfo.pg);
150 
151  if (!pg_info->toposchema_name) { /* pseudo-topology */
152  return V2_write_line_sfa(Map, type, points, cats);
153  }
154 
155  /* PostGIS Topology */
156  return write_line_tp(Map, type, FALSE, points, cats);
157 #else
158  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
159  return -1;
160 #endif
161 }
162 
163 /*!
164  \brief Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only)
165 
166  Only for simple feature access. PostGIS Topology requires level 2.
167 
168  \todo Use UPDATE statement ?
169 
170  \param Map pointer to Map_info structure
171  \param offset feature offset
172  \param type feature type (GV_POINT, GV_LINE, ...)
173  \param points feature geometry
174  \param cats feature categories
175 
176  \return feature offset (rewritten feature)
177  \return -1 on error
178  */
179 off_t V1_rewrite_line_pg(struct Map_info * Map,
180  off_t offset, int type,
181  const struct line_pnts * points,
182  const struct line_cats * cats)
183 {
184  G_debug(3, "V1_rewrite_line_pg(): type=%d offset=%"PRI_OFF_T,
185  type, offset);
186 #ifdef HAVE_POSTGRES
187  if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
188  G_warning(_("Unable to rewrite feature (incompatible feature types)"));
189  return -1;
190  }
191 
192  /* delete old */
193  V1_delete_line_pg(Map, offset);
194 
195  return V1_write_line_pg(Map, type, points, cats);
196 #else
197  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
198  return -1;
199 #endif
200 }
201 
202 /*!
203  \brief Rewrites feature at topological level (PostGIS interface, internal use only)
204 
205  Note: Topology must be built at level >= GV_BUILD_BASE
206 
207  \todo Handle also categories
208  \todo Store original geometry in tmp table for restore
209 
210  \param Map pointer to Map_info structure
211  \param line feature id
212  \param type feature type (GV_POINT, GV_LINE, ...)
213  \param points feature geometry
214  \param cats feature categories
215 
216  \return offset where feature was rewritten
217  \return -1 on error
218 */
219 off_t V2_rewrite_line_pg(struct Map_info *Map, off_t line, int type,
220  const struct line_pnts *points, const struct line_cats *cats)
221 {
222  G_debug(3, "V2_rewrite_line_pg(): line=%d type=%d",
223  (int)line, type);
224 #ifdef HAVE_POSTGRES
225  const char *schema_name, *table_name, *keycolumn;
226  char *stmt, *geom_data;
227 
228  struct Format_info_pg *pg_info;
229  struct P_line *Line;
230  off_t offset;
231 
232  geom_data = NULL;
233  stmt = NULL;
234  pg_info = &(Map->fInfo.pg);
235 
236  if (line < 1 || line > Map->plus.n_lines) {
237  G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
238  return -1;
239  }
240 
241  Line = Map->plus.Line[line];
242  if (Line == NULL) {
243  G_warning(_("Attempt to access dead feature %d"), (int)line);
244  return -1;
245  }
246  offset = Line->offset;
247 
248  if (!(Map->plus.update_cidx)) {
249  Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
250  }
251 
252  if (!Points)
253  Points = Vect_new_line_struct();
254 
255  if (type != V2_read_line_pg(Map, Points, NULL, line)) {
256  G_warning(_("Unable to rewrite feature (incompatible feature types)"));
257  return -1;
258  }
259 
260  /* remove line from topology */
261  if (0 != delete_line_from_topo_pg(Map, line, type, Points))
262  return -1;
263 
264  if (pg_info->toposchema_name) { /* PostGIS Topology */
265  schema_name = pg_info->toposchema_name;
266  if (type & GV_POINTS) {
267  table_name = keycolumn = "node";
268  }
269  else {
270  table_name = "edge_data";
271  keycolumn = "edge";
272  }
273  }
274  else { /* simple features access */
275  schema_name = pg_info->schema_name;
276  table_name = pg_info->table_name;
277  keycolumn = pg_info->fid_column;
278  }
279 
280  geom_data = line_to_wkb(pg_info, &points, 1, type, Map->head.with_z);
281  G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET geom = '%s'::GEOMETRY WHERE %s_id = %ld",
282  schema_name, table_name, geom_data, keycolumn, line);
283  G_free(geom_data);
284 
285  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
286  G_warning(_("Unable to rewrite feature %d"), (int)line);
287  Vect__execute_pg(pg_info->conn, "ROLLBACK");
288  return -1;
289  }
290 
291  /* update topology
292  note: offset is not changed */
293  return add_line_to_topo_pg(Map, offset, type, points);
294 #else
295  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
296  return -1;
297 #endif
298 }
299 
300 /*!
301  \brief Deletes feature at the given offset (level 1)
302 
303  Only for simple feature access. PostGIS Topology requires level 2.
304 
305  \param Map pointer Map_info structure
306  \param offset feature offset
307 
308  \return 0 on success
309  \return -1 on error
310  */
311 int V1_delete_line_pg(struct Map_info *Map, off_t offset)
312 {
313 #ifdef HAVE_POSTGRES
314  long fid;
315  char stmt[DB_SQL_MAX];
316 
317  struct Format_info_pg *pg_info;
318 
319  pg_info = &(Map->fInfo.pg);
320 
321  if (!pg_info->conn || !pg_info->table_name) {
322  G_warning(_("No connection defined"));
323  return -1;
324  }
325 
326  if (offset >= pg_info->offset.array_num) {
327  G_warning(_("Invalid offset (%ld)"), offset);
328  return -1;
329  }
330 
331  fid = pg_info->offset.array[offset];
332 
333  G_debug(3, "V1_delete_line_pg(): offset = %lu -> fid = %ld",
334  (unsigned long)offset, fid);
335 
336  if (!pg_info->inTransaction) {
337  /* start transaction */
338  pg_info->inTransaction = TRUE;
339  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
340  return -1;
341  }
342 
343  sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
344  pg_info->table_name, pg_info->fid_column, fid);
345  G_debug(3, "SQL: %s", stmt);
346 
347  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
348  G_warning(_("Unable to delete feature"));
349  Vect__execute_pg(pg_info->conn, "ROLLBACK");
350  return -1;
351  }
352 
353  return 0;
354 #else
355  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
356  return -1;
357 #endif
358 }
359 
360 /*!
361  \brief Deletes feature on topological level (PostGIS interface)
362 
363  Note: Topology must be built at level >= GV_BUILD_BASE
364 
365  Calls V2_delete_line_sfa() for simple feature access.
366 
367  \param Map pointer to Map_info structure
368  \param line feature id to be deleted
369 
370  \return 0 on success
371  \return -1 on error
372 */
373 int V2_delete_line_pg(struct Map_info *Map, off_t line)
374 {
375 #ifdef HAVE_POSTGRES
376  int ret;
377  struct Format_info_pg *pg_info;
378 
379  pg_info = &(Map->fInfo.pg);
380 
381  if (line < 1 || line > Map->plus.n_lines) {
382  G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
383  return -1;
384  }
385 
386  if (!pg_info->toposchema_name) { /* pseudo-topology */
387  return V2_delete_line_sfa(Map, line);
388  }
389  else { /* PostGIS topology */
390  int type;
391  char stmt[DB_SQL_MAX];
392  const char *table_name, *keycolumn;
393 
394  struct P_line *Line;
395 
396  if (line < 1 || line > Map->plus.n_lines) {
397  G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
398  return -1;
399  }
400 
401  Line = Map->plus.Line[line];
402  if (!Line) {
403  G_warning(_("Attempt to access dead feature %d"), (int)line);
404  return -1;
405  }
406 
407  if (!(Map->plus.update_cidx)) {
408  Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
409  }
410 
411  Vect__execute_pg(pg_info->conn, "BEGIN");
412 
413  if (Line->type & GV_POINTS) {
414  table_name = keycolumn = "node";
415  }
416  else {
417  table_name = "edge_data";
418  keycolumn = "edge";
419 
420  /* first remove references to this edge */
421  /* (1) left next edge */
422  sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_left_edge = edge_id, "
423  "next_left_edge = -edge_id WHERE abs_next_left_edge = %d",
424  pg_info->toposchema_name, table_name, (int)Line->offset);
425  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
426  Vect__execute_pg(pg_info->conn, "ROLLBACK");
427  return -1;
428  }
429 
430  /* (2) right next edge */
431  sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_right_edge = edge_id, "
432  "next_right_edge = edge_id WHERE abs_next_right_edge = %d",
433  pg_info->toposchema_name, table_name, (int)Line->offset);
434  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
435  Vect__execute_pg(pg_info->conn, "ROLLBACK");
436  return -1;
437  }
438 
439  }
440 
441  /* read the line */
442  if (!Points)
443  Points = Vect_new_line_struct();
444 
445  type = V2_read_line_pg(Map, Points, NULL, line);
446  if (type < 0)
447  return -1;
448 
449  /* delete record from topology table */
450  sprintf(stmt, "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %d",
451  pg_info->toposchema_name, table_name, keycolumn, (int)Line->offset);
452  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
453  G_warning(_("Unable to delete feature (%s) %d"), keycolumn,
454  (int)line);
455  Vect__execute_pg(pg_info->conn, "ROLLBACK");
456  return -1;
457  }
458 
459  if (pg_info->cache.ctype == CACHE_MAP) {
460  /* delete from cache */
461 
462  Vect_destroy_line_struct(pg_info->cache.lines[line-1]);
463  pg_info->cache.lines[line-1] = NULL;
464  pg_info->cache.lines_types[line-1] = 0;
465  pg_info->cache.lines_cats[line-1] = 0;
466  }
467 
468  /* update topology */
469  ret = delete_line_from_topo_pg(Map, line, type, Points);
470 
471  if (ret == 0)
472  Vect__execute_pg(pg_info->conn, "COMMIT");
473 
474  return ret;
475  }
476 #else
477  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
478  return -1;
479 #endif
480 }
481 
482 #ifdef HAVE_POSTGRES
483 /*!
484  \brief Writes node on topological level (PostGIS Topology
485  interface, internal use only)
486 
487  The vector map must be open on level 2 at least with
488  GV_BUILD_BASE. PostGIS Topology schema must be defined.
489 
490  \param Map pointer to Map_info structure
491  \param node node id (starts at 1)
492  \param points pointer to line_pnts structure
493 
494  \return 0 on success
495  \return -1 on error
496 */
497 off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
498 {
499  struct Format_info_pg *pg_info;
500 
501  pg_info = &(Map->fInfo.pg);
502 
503  if (!pg_info->toposchema_name)
504  return -1; /* PostGIS Topology required */
505 
506  return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
507 }
508 
509 /*!
510  \brief Writes area on topological level (PostGIS Simple Features
511  interface, internal use only)
512 
513  \param Map pointer to Map_info structure
514  \param points feature geometry (exterior + interior rings)
515  \param nparts number of parts including exterior ring
516  \param cats feature categories
517 
518  \return feature offset
519  \return -1 on error
520 */
521 off_t V2__write_area_pg(struct Map_info *Map,
522  const struct line_pnts **points, int nparts,
523  const struct line_cats *cats)
524 {
525  return write_line_sf(Map, GV_BOUNDARY, points, nparts, cats);
526 }
527 
528 /*!
529  \brief Updates simple features geometry from GRASS-like topo
530 
531  \param Map pointer to Map_info structure
532  \param points feature geometry (exterior + interior rings)
533  \param nparts number of parts including exterior ring
534  \param cat area category
535 
536  \return 0 on success
537  \return -1 on error
538 */
539 int V2__update_area_pg(struct Map_info *Map,
540  const struct line_pnts **points, int nparts,
541  int cat)
542 {
543  int part, npoints;
544  char *stmt, *geom_data;
545 
546  struct Format_info_pg *pg_info;
547 
548  pg_info = &(Map->fInfo.pg);
549 
550  for (part = 0; part < nparts; part++) {
551  npoints = points[part]->n_points - 1;
552  if (points[part]->x[0] != points[part]->x[npoints] ||
553  points[part]->y[0] != points[part]->y[npoints] ||
554  points[part]->z[0] != points[part]->z[npoints]) {
555  G_warning(_("Boundary is not closed. Skipping."));
556  return -1;
557  }
558  }
559 
560  geom_data = line_to_wkb(pg_info, points, nparts, GV_AREA, Vect_is_3d(Map));
561  if (!geom_data)
562  return -1;
563 
564  stmt = NULL;
565  G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET %s = '%s'::GEOMETRY WHERE %s = %d",
566  pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
567  geom_data, pg_info->fid_column,
568  cat);
569  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
570  /* rollback transaction */
571  Vect__execute_pg(pg_info->conn, "ROLLBACK");
572  G_free(geom_data);
573  G_free(stmt);
574  return -1;
575  }
576 
577  G_free(geom_data);
578  G_free(stmt);
579 
580  return 0;
581 }
582 
583 /*!
584  \brief Create new feature table
585 
586  \param pg_info pointer to Format_info_pg
587 
588  \return -1 on error
589  \return 0 on success
590 */
591 int create_table(struct Format_info_pg *pg_info)
592 {
593  int spatial_index, primary_key;
594  char stmt[DB_SQL_MAX];
595  char *geom_type, *def_file;
596 
597  struct field_info *Fi;
598 
599  PGresult *result;
600 
601  def_file = getenv("GRASS_VECTOR_PGFILE");
602 
603  /* by default create spatial index & add primary key */
604  spatial_index = primary_key = TRUE;
605  if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
606  FILE *fp;
607  const char *p;
608 
609  struct Key_Value *key_val;
610 
611  fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
612  if (!fp) {
613  G_warning(_("Unable to open PG file"));
614  }
615  else {
616  key_val = G_fread_key_value(fp);
617  fclose(fp);
618 
619  /* disable spatial index ? */
620  p = G_find_key_value("spatial_index", key_val);
621  if (p && G_strcasecmp(p, "no") == 0)
622  spatial_index = FALSE;
623 
624  /* disable primary key ? */
625  p = G_find_key_value("primary_key", key_val);
626  if (p && G_strcasecmp(p, "no") == 0)
627  primary_key = FALSE;
628 
629  G_free_key_value(key_val);
630  }
631  }
632 
633  /* create schema if not exists */
634  if (G_strcasecmp(pg_info->schema_name, "public") != 0) {
635  if (check_schema(pg_info) != 0)
636  return -1;
637  }
638 
639  /* prepare CREATE TABLE statement */
640  sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL%s, %s INTEGER",
641  pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
642  primary_key ? " PRIMARY KEY" : "", GV_KEY_COLUMN);
643 
644  Fi = pg_info->fi;
645 
646  if (Fi) {
647  /* append attributes */
648  int col, ncols, sqltype, length;
649  char stmt_col[DB_SQL_MAX];
650  const char *colname;
651 
652  dbString dbtable_name;
653 
654  dbDriver *driver;
655  dbTable *table;
656  dbColumn *column;
657 
658  db_init_string(&dbtable_name);
659 
660  driver = open_db(pg_info);
661  if (driver == NULL)
662  return -1;
663 
664  /* describe table */
665  db_set_string(&dbtable_name, Fi->table);
666  if (db_describe_table(driver, &dbtable_name, &table) != DB_OK) {
667  G_warning(_("Unable to describe table <%s>"),
668  Fi->table);
670  pg_info->dbdriver = NULL;
671  return -1;
672  }
673  ncols = db_get_table_number_of_columns(table);
674 
675  G_debug(3,
676  "copying attributes: driver = %s database = %s table = %s cols = %d",
677  Fi->driver, Fi->database, Fi->table, ncols);
678 
679  for (col = 0; col < ncols; col++) {
680  column = db_get_table_column(table, col);
681  colname = db_get_column_name(column);
682  sqltype = db_get_column_sqltype(column);
683  length = db_get_column_length(column);
684 
685  G_debug(3, "\tcolumn = %d name = %s type = %d length = %d",
686  col, colname, sqltype, length);
687 
688  if (G_strcasecmp(pg_info->fid_column, colname) == 0 ||
689  G_strcasecmp(GV_KEY_COLUMN, colname) == 0) {
690  /* skip fid column if exists */
691  G_debug(3, "\t%s skipped", colname);
692  continue;
693  }
694 
695  /* append column */
696  sprintf(stmt_col, ",\"%s\" %s", colname, db_sqltype_name(sqltype));
697  strcat(stmt, stmt_col);
698  if (sqltype == DB_SQL_TYPE_CHARACTER) {
699  /* length only for string columns */
700  sprintf(stmt_col, "(%d)", length);
701  strcat(stmt, stmt_col);
702  }
703  }
704 
705  db_free_string(&dbtable_name);
706  }
707  strcat(stmt, ")"); /* close CREATE TABLE statement */
708 
709  /* begin transaction (create table) */
710  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
711  return -1;
712  }
713 
714  /* create table */
715  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
716  Vect__execute_pg(pg_info->conn, "ROLLBACK");
717  return -1;
718  }
719 
720  /* determine geometry type (string) */
721  switch (pg_info->feature_type) {
722  case (SF_POINT):
723  geom_type = "POINT";
724  break;
725  case (SF_LINESTRING):
726  geom_type = "LINESTRING";
727  break;
728  case (SF_POLYGON):
729  geom_type = "POLYGON";
730  break;
731  case (SF_POLYGON25D):
732  geom_type = "POLYGONZ";
733  break;
734  case (SF_GEOMETRY):
735  geom_type = "GEOMETRY";
736  break;
737  default:
738  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
739  Vect__execute_pg(pg_info->conn, "ROLLBACK");
740  return -1;
741  }
742 
743  /* add geometry column */
744  sprintf(stmt, "SELECT AddGeometryColumn('%s', '%s', "
745  "'%s', %d, '%s', %d)",
746  pg_info->schema_name, pg_info->table_name,
747  pg_info->geom_column, pg_info->srid,
748  geom_type, pg_info->coor_dim);
749  G_debug(2, "SQL: %s", stmt);
750  result = PQexec(pg_info->conn, stmt);
751 
752  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
753  G_warning("%s", PQresultErrorMessage(result));
754  PQclear(result);
755  Vect__execute_pg(pg_info->conn, "ROLLBACK");
756  return -1;
757  }
758 
759  /* create indices
760  - GV_KEY_COLUMN
761  - geometry column
762  */
763  sprintf(stmt,
764  "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" (%s)",
765  pg_info->table_name, GV_KEY_COLUMN,
766  pg_info->schema_name, pg_info->table_name,
767  GV_KEY_COLUMN);
768  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
769  Vect__execute_pg(pg_info->conn, "ROLLBACK");
770  return -1;
771  }
772 
773  if (spatial_index) {
774  G_verbose_message(_("Building spatial index on <%s>..."),
775  pg_info->geom_column);
776  sprintf(stmt,
777  "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
778  pg_info->table_name, pg_info->geom_column,
779  pg_info->schema_name, pg_info->table_name,
780  pg_info->geom_column);
781 
782  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
783  Vect__execute_pg(pg_info->conn, "ROLLBACK");
784  return -1;
785  }
786  }
787 
788  /* close transaction (create table) */
789  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
790  return -1;
791  }
792 
793  return 0;
794 }
795 
796 /*!
797  \brief Creates new schema for feature table if not exists
798 
799  \param pg_info pointer to Format_info_pg
800 
801  \return -1 on error
802  \return 0 on success
803 */
804 int check_schema(const struct Format_info_pg *pg_info)
805 {
806  int i, found, nschema;
807  char stmt[DB_SQL_MAX];
808 
809  PGresult *result;
810 
811  if (!pg_info->conn || !pg_info->table_name) {
812  G_warning(_("No connection defined"));
813  return -1;
814  }
815 
816  /* add geometry column */
817  sprintf(stmt, "SELECT nspname FROM pg_namespace");
818  G_debug(2, "SQL: %s", stmt);
819  result = PQexec(pg_info->conn, stmt);
820 
821  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
822  PQclear(result);
823  Vect__execute_pg(pg_info->conn, "ROLLBACK");
824  return -1;
825  }
826 
827  found = FALSE;
828  nschema = PQntuples(result);
829  for (i = 0; i < nschema && !found; i++) {
830  if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
831  found = TRUE;
832  }
833 
834  PQclear(result);
835 
836  if (!found) {
837  sprintf(stmt, "CREATE SCHEMA %s", pg_info->schema_name);
838  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
839  Vect__execute_pg(pg_info->conn, "ROLLBACK");
840  return -1;
841  }
842  G_warning(_("Schema <%s> doesn't exist, created"),
843  pg_info->schema_name);
844  }
845 
846  return 0;
847 }
848 
849 /*!
850  \brief Create new PostGIS topology schema
851 
852  - create topology schema
853  - add topology column to the feature table
854 
855  \todo Add constraints for grass-like tables
856 
857  \param pg_info pointer to Format_info_pg
858 
859  \return 0 on success
860  \return 1 topology disable, nothing to do
861  \return -1 on failure
862 */
863 int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
864 {
865  double tolerance;
866  char stmt[DB_SQL_MAX];
867  char *def_file;
868 
869  def_file = getenv("GRASS_VECTOR_PGFILE");
870 
871  /* read default values from PG file*/
872  tolerance = 0.;
873  if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
874  FILE *fp;
875  const char *p;
876 
877  struct Key_Value *key_val;
878 
879  fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
880  if (!fp) {
881  G_fatal_error(_("Unable to open PG file"));
882  }
883  key_val = G_fread_key_value(fp);
884  fclose(fp);
885 
886  /* tolerance */
887  p = G_find_key_value("topo_tolerance", key_val);
888  if (p)
889  tolerance = atof(p);
890  G_debug(1, "PG: tolerance: %f", tolerance);
891 
892  /* topogeom column */
893  p = G_find_key_value("topogeom_name", key_val);
894  if (p)
895  pg_info->topogeom_column = G_store(p);
896  else
897  pg_info->topogeom_column = G_store(TOPOGEOM_COLUMN);
898  G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
899 
900  /* topo-geo only (default: no) */
901  p = G_find_key_value("topo_geo_only", key_val);
902  if (p && G_strcasecmp(p, "yes") == 0)
903  pg_info->topo_geo_only = TRUE;
904  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
905 
906  /* build simple features from topogeometry data */
907  p = G_find_key_value("simple_feature", key_val);
908  if (p && G_strcasecmp(p, "yes") == 0)
909  pg_info->topo_geo_only = TRUE;
910  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
911 
912  G_free_key_value(key_val);
913  }
914 
915  /* begin transaction (create topo schema) */
916  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
917  return -1;
918  }
919 
920  /* create topology schema */
921  G_verbose_message(_("Creating topology schema <%s>..."),
922  pg_info->toposchema_name);
923  sprintf(stmt, "SELECT topology.createtopology('%s', "
924  "find_srid('%s', '%s', '%s'), %f, '%s')",
925  pg_info->toposchema_name, pg_info->schema_name,
926  pg_info->table_name, pg_info->geom_column, tolerance,
927  with_z == WITH_Z ? "t" : "f");
928  pg_info->toposchema_id = Vect__execute_get_value_pg(pg_info->conn, stmt);
929  if (pg_info->toposchema_id == -1) {
930  Vect__execute_pg(pg_info->conn, "ROLLBACK");
931  return -1;
932  }
933 
934  /* add topo column to the feature table */
935  G_verbose_message(_("Adding new topology column <%s>..."),
936  pg_info->topogeom_column);
937  sprintf(stmt, "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
938  "'%s', '%s')", pg_info->toposchema_name, pg_info->schema_name,
939  pg_info->table_name, pg_info->topogeom_column,
940  get_sftype(pg_info->feature_type));
941  if (-1 == Vect__execute_get_value_pg(pg_info->conn, stmt)) {
942  Vect__execute_pg(pg_info->conn, "ROLLBACK");
943  return -1;
944  }
945 
946  /* create index on topo column */
947  sprintf(stmt, "CREATE INDEX \"%s_%s_%s_idx\" ON \"%s\".\"%s\" (((%s).id))",
948  pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
949  pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column);
950  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
951  Vect__execute_pg(pg_info->conn, "ROLLBACK");
952  return -1;
953  }
954 
955  /* change constraints to deferrable initially deferred */
956  if (!pg_info->topo_geo_only) {
957  if (-1 == set_constraint_to_deferrable(pg_info, "node", "face_exists",
958  "containing_face", "face", "face_id") ||
959  -1 == set_constraint_to_deferrable(pg_info, "edge_data", "end_node_exists",
960  "end_node", "node", "node_id") ||
961  -1 == set_constraint_to_deferrable(pg_info, "edge_data", "left_face_exists",
962  "left_face", "face", "face_id") ||
963  -1 == set_constraint_to_deferrable(pg_info, "edge_data", "right_face_exists",
964  "right_face", "face", "face_id") ||
965  -1 == set_constraint_to_deferrable(pg_info, "edge_data", "start_node_exists",
966  "start_node", "node", "node_id"))
967  return -1;
968  }
969 
970  /* create additional tables in topological schema to store
971  GRASS topology in DB */
972  if (!pg_info->topo_geo_only) {
973  /* (1) create 'node_grass' (see P_node struct)
974 
975  todo: add constraints for lines and angles
976  */
977  sprintf(stmt, "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
978  "lines integer[], angles float[])", pg_info->toposchema_name, TOPO_TABLE_NODE);
979  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
980  Vect__execute_pg(pg_info->conn, "ROLLBACK");
981  return -1;
982  }
983 
984  sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
985  "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id) "
986  "DEFERRABLE INITIALLY DEFERRED",
987  pg_info->toposchema_name, TOPO_TABLE_NODE, pg_info->toposchema_name);
988  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
989  Vect__execute_pg(pg_info->conn, "ROLLBACK");
990  return -1;
991  }
992 
993  /* (2) create 'line_grass' (see P_line struct)
994 
995  */
996  sprintf(stmt, "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
997  "left_area integer, right_area integer)",
998  pg_info->toposchema_name, TOPO_TABLE_LINE);
999  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1000  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1001  return -1;
1002  }
1003 
1004  sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT line_exists "
1005  "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
1006  "DEFERRABLE INITIALLY DEFERRED",
1007  pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->toposchema_name);
1008  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1009  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1010  return -1;
1011  }
1012 
1013  /* (3) create 'area_grass' (see P_area struct)
1014 
1015  todo: add constraints for lines, centtroid and isles
1016  */
1017  sprintf(stmt, "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
1018  "lines integer[], centroid integer, isles integer[])",
1019  pg_info->toposchema_name, TOPO_TABLE_AREA);
1020  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1021  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1022  return -1;
1023  }
1024 
1025  /* (4) create 'isle_grass' (see P_isle struct)
1026 
1027  todo: add constraints for lines and area
1028  */
1029  sprintf(stmt, "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
1030  "lines integer[], area integer)",
1031  pg_info->toposchema_name, TOPO_TABLE_ISLE);
1032  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1033  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1034  return -1;
1035  }
1036  }
1037 
1038  /* close transaction (create topo schema) */
1039  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
1040  return -1;
1041  }
1042 
1043  return 0;
1044 }
1045 
1046 /*!
1047  \brief Create new PostGIS layer in given database (internal use only)
1048 
1049  V1_open_new_pg() must be called before this function.
1050 
1051  List of currently supported types:
1052  - GV_POINT (SF_POINT)
1053  - GV_LINE (SF_LINESTRING)
1054  - GV_BOUNDARY (SF_POLYGON)
1055 
1056  When PostGIS Topology the map level is updated to topological level
1057  and build level set to GV_BUILD_BASE.
1058 
1059  \param[in,out] Map pointer to Map_info structure
1060  \param type feature type (GV_POINT, GV_LINE, ...)
1061 
1062  \return 0 success
1063  \return -1 error
1064  */
1065 int create_pg_layer(struct Map_info *Map, int type)
1066 {
1067  int ndblinks;
1068 
1069  struct Format_info_pg *pg_info;
1070 
1071  pg_info = &(Map->fInfo.pg);
1072  if (!pg_info->conninfo) {
1073  G_warning(_("Connection string not defined"));
1074  return -1;
1075  }
1076 
1077  if (!pg_info->table_name) {
1078  G_warning(_("PostGIS feature table not defined"));
1079  return -1;
1080  }
1081 
1082  G_debug(1, "Vect__open_new_pg(): conninfo='%s' table='%s' -> type = %d",
1083  pg_info->conninfo, pg_info->table_name, type);
1084 
1085  /* determine geometry type */
1086 
1087  switch (type) {
1088  case GV_POINT:
1089  case GV_CENTROID:
1090  pg_info->feature_type = SF_POINT;
1091  break;
1092  case GV_LINE:
1093  case GV_BOUNDARY:
1094  pg_info->feature_type = SF_LINESTRING;
1095  break;
1096  case GV_AREA:
1097  pg_info->feature_type = SF_POLYGON;
1098  break;
1099  case GV_FACE:
1100  pg_info->feature_type = SF_POLYGON25D;
1101  break;
1102  case -2:
1103  pg_info->feature_type = SF_GEOMETRY;
1104  break;
1105  default:
1106  G_warning(_("Unsupported geometry type (%d)"), type);
1107  return -1;
1108  }
1109 
1110  /* coordinate dimension */
1111  pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
1112 
1113  /* create new PostGIS table */
1114  ndblinks = Vect_get_num_dblinks(Map);
1115  if (ndblinks > 0) {
1116  pg_info->fi = Vect_get_dblink(Map, 0); /* TODO: support more layers */
1117  if (pg_info->fi) {
1118  if (ndblinks > 1)
1119  G_warning(_("More layers defined, using driver <%s> and "
1120  "database <%s>"),
1121  pg_info->fi->driver, pg_info->fi->database);
1122  }
1123  else {
1124  G_warning(_("Database connection not defined. "
1125  "Unable to write attributes."));
1126  }
1127  }
1128 
1129  /* create new feature table */
1130  if (create_table(pg_info) == -1) {
1131  G_warning(_("Unable to create new PostGIS feature table"));
1132  return -1;
1133  }
1134 
1135  /* create new topology schema (if PostGIS topology support is enabled) */
1136  if (pg_info->toposchema_name) {
1137  /* force topological level */
1138  Map->level = LEVEL_2;
1139  Map->plus.built = GV_BUILD_BASE;
1140 
1141  /* track updated features, used in V2__add_line_to_topo_nat() */
1142  Vect_set_updated(Map, TRUE);
1143 
1144  if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
1145  G_warning(_("Unable to create new PostGIS topology schema"));
1146  return -1;
1147  }
1148  }
1149 
1150  return 0;
1151 }
1152 
1153 /*!
1154  \brief Get simple feature type as a string
1155 
1156  Used for AddTopoGeometryColumn().
1157 
1158  Valid types:
1159  - SF_POINT
1160  - SF_LINESTRING
1161  - SF_POLYGON
1162 
1163  \return string with feature type
1164  \return empty string
1165 */
1166 char *get_sftype(SF_FeatureType sftype)
1167 {
1168  if (sftype == SF_POINT)
1169  return "POINT";
1170  else if (sftype == SF_LINESTRING)
1171  return "LINE";
1172  else if (sftype == SF_POLYGON)
1173  return "POLYGON";
1174  else if (sftype == SF_GEOMETRY || sftype == SF_GEOMETRYCOLLECTION)
1175  return "COLLECTION";
1176  else
1177  G_warning(_("Unsupported feature type %d"), sftype);
1178 
1179  return "";
1180 }
1181 
1182 /*!
1183  \brief Write vector features as PostGIS simple feature element
1184 
1185  \param Map pointer to Map_info structure
1186  \param type feature type (GV_POINT, GV_LINE, ...)
1187  \param points feature geometry (exterior + interior rings for polygonsx)
1188  \param nparts number of parts
1189  \param cats feature categories
1190 
1191  \return feature offset
1192  \return -1 on error
1193 */
1194 off_t write_line_sf(struct Map_info *Map, int type,
1195  const struct line_pnts **points, int nparts,
1196  const struct line_cats *cats)
1197 {
1198  int cat;
1199  off_t offset;
1200 
1201  SF_FeatureType sf_type;
1202 
1203  struct Format_info_pg *pg_info;
1204  struct Format_info_offset *offset_info;
1205 
1206  pg_info = &(Map->fInfo.pg);
1207  offset_info = &(pg_info->offset);
1208 
1209  if (nparts < 1)
1210  return -1;
1211 
1212  /* check required PG settings */
1213  if (!pg_info->conn) {
1214  G_warning(_("No connection defined"));
1215  return -1;
1216  }
1217  if (!pg_info->table_name) {
1218  G_warning(_("PostGIS feature table not defined"));
1219  return -1;
1220  }
1221 
1222  /* create PostGIS table if doesn't exist */
1223  if (pg_info->feature_type == SF_GEOMETRY) {
1224  if (create_pg_layer(Map, type) < 0)
1225  return -1;
1226  }
1227 
1228  /* get category & check for attributes */
1229  cat = -1;
1230  if (cats->n_cats > 0) {
1231  int field;
1232 
1233  if (pg_info->fi)
1234  field = pg_info->fi->number;
1235  else
1236  field = 1;
1237 
1238  if (!Vect_cat_get(cats, field, &cat))
1239  G_warning(_("No category defined for layer %d"), field);
1240  if (cats->n_cats > 1) {
1241  G_warning(_("Feature has more categories, using "
1242  "category %d (from layer %d)"),
1243  cat, field);
1244  }
1245  }
1246 
1247  sf_type = pg_info->feature_type;
1248 
1249  /* determine matching PostGIS feature geometry type */
1250  if (type & (GV_POINT | GV_KERNEL)) {
1251  if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
1252  G_warning(_("Point skipped (output feature type: %s)"),
1254  return 0;
1255  }
1256  }
1257  else if (type & GV_LINE) {
1258  if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
1259  G_warning(_("Line skipped (output feature type: %s)"),
1261  return 0;
1262  }
1263  }
1264  else if (type & GV_CENTROID) {
1265  if (sf_type != SF_POLYGON && sf_type != SF_POINT) {
1266  G_warning(_("Centroid skipped (output feature type: %s)"),
1268  return 0;
1269  }
1270  }
1271  else if (type & GV_BOUNDARY) {
1272  if (sf_type != SF_POLYGON && sf_type != SF_LINESTRING) {
1273  G_warning(_("Boundary skipped (output feature type: %s)"),
1275  return 0;
1276  }
1277  }
1278  else if (type & GV_FACE) {
1279  if (sf_type != SF_POLYGON25D) {
1280  G_warning(_("Face skipped (output feature type: %s)"),
1282  return 0;
1283  }
1284  }
1285  else {
1286  G_warning(_("Unsupported feature type %d"), type);
1287  return -1;
1288  }
1289 
1290  G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d",
1291  type, points[0]->n_points, cat);
1292 
1293  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1294  /* skip this check when writing PostGIS topology */
1295  int part, npoints;
1296 
1297  for (part = 0; part < nparts; part++) {
1298  npoints = points[part]->n_points - 1;
1299  if (points[part]->x[0] != points[part]->x[npoints] ||
1300  points[part]->y[0] != points[part]->y[npoints] ||
1301  points[part]->z[0] != points[part]->z[npoints]) {
1302  G_warning(_("Boundary is not closed. Skipping."));
1303  return -1;
1304  }
1305  }
1306  }
1307 
1308  /* write feature's geometry and fid */
1309  if (-1 == write_feature(Map, -1, type, points, nparts, cat)) {
1310  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1311  return -1;
1312  }
1313 
1314  /* update offset array */
1315  if (offset_info->array_num >= offset_info->array_alloc) {
1316  offset_info->array_alloc += 1000;
1317  offset_info->array = (int *)G_realloc(offset_info->array,
1318  offset_info->array_alloc *
1319  sizeof(int));
1320  }
1321  offset = offset_info->array_num;
1322 
1323  offset_info->array[offset_info->array_num++] = cat;
1324  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1325  /* register first part in offset array */
1326  offset_info->array[offset_info->array_num++] = 0;
1327  }
1328  G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
1329  (unsigned long)offset, offset_info->array_num, cat);
1330 
1331  return offset;
1332 }
1333 
1334 /*!
1335  \brief Write vector feature in PostGIS topology schema and
1336  updates internal topology structures
1337 
1338  \param Map vector map
1339  \param type feature type to be written
1340  \param points feature geometry
1341  \param is_node TRUE for nodes (written as points)
1342 
1343  \return feature id (build level >= GV_BUILD_BASE otherwise 0)
1344  \return 0 for nodes
1345  \return -1 on error
1346 */
1347 off_t write_line_tp(struct Map_info *Map, int type, int is_node,
1348  const struct line_pnts *points,
1349  const struct line_cats *cats)
1350 {
1351  int line, cat, line_id;
1352 
1353  struct Format_info_pg *pg_info;
1354  struct Plus_head *plus;
1355  struct field_info *Fi;
1356 
1357  pg_info = &(Map->fInfo.pg);
1358  plus = &(Map->plus);
1359 
1360  if (!(plus->update_cidx)) {
1361  plus->cidx_up_to_date = FALSE; /* category index will be outdated */
1362  }
1363 
1364  /* check type for nodes */
1365  if (is_node && type != GV_POINT) {
1366  G_warning(_("Invalid feature type (%d) for nodes"), type);
1367  return -1;
1368  }
1369 
1370  /* check required PG settings */
1371  if (!pg_info->conn) {
1372  G_warning(_("No connection defined"));
1373  return -1;
1374  }
1375  if (!pg_info->table_name) {
1376  G_warning(_("PostGIS feature table not defined"));
1377  return -1;
1378  }
1379  if (!pg_info->toposchema_name) {
1380  G_warning(_("PostGIS topology schema not defined"));
1381  return -1;
1382  }
1383 
1384  /* create PostGIS table if doesn't exist */
1385  if (pg_info->feature_type == SF_GEOMETRY) {
1386  if (create_pg_layer(Map, type) < 0)
1387  return -1;
1388  }
1389 
1390  if (!points)
1391  return 0;
1392 
1393  G_debug(3, "write_line_pg(): type = %d n_points = %d",
1394  type, points->n_points);
1395 
1396  Fi = pg_info->fi;
1397 
1398  cat = -1;
1399  if (cats && cats->n_cats > 0) {
1400  if (Fi) {
1401  if (!pg_info->dbdriver)
1402  open_db(pg_info);
1403  if (!Vect_cat_get(cats, Fi->number, &cat))
1404  G_warning(_("No category defined for layer %d"), Fi->number);
1405  if (cats->n_cats > 1) {
1406  G_warning(_("Feature has more categories, using "
1407  "category %d (from layer %d)"),
1408  cat, cats->field[0]);
1409  }
1410  }
1411  /* assume layer=1 */
1412  Vect_cat_get(cats, 1, &cat);
1413  }
1414 
1415  /* update GRASS topology before writing PostGIS feature */
1416  line = 0;
1417  if (plus->built >= GV_BUILD_BASE) {
1418  if (is_node) {
1419  /* nodes are given with negative id */
1420  line = -1 * dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
1421  }
1422  else {
1423  off_t offset;
1424 
1425  /* better is probably to check nextval directly */
1426  if (type & GV_POINTS) {
1427  offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
1428  offset += Vect_get_num_nodes(Map); /* nodes are also stored in 'node' table */
1429  }
1430  else { /* LINES */
1431  offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
1432  }
1433 
1434  line = add_line_to_topo_pg(Map, offset, type, points);
1435  }
1436  }
1437 
1438  /* write new feature to PostGIS
1439  - feature table for simple features
1440  - feature table and topo schema for topological access
1441  */
1442  line_id = write_feature(Map, line, type, &points, 1, cat);
1443  if (line_id < 0) {
1444  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1445  return -1;
1446  }
1447 
1448  if (pg_info->cache.ctype == CACHE_MAP) {
1449  /* add line to the cache */
1450  Vect__reallocate_cache(&(pg_info->cache), 1, TRUE);
1451  pg_info->cache.lines[line-1] = Vect_new_line_struct();
1452  pg_info->cache.lines_types[line-1] = type;
1453  pg_info->cache.lines_cats[line-1] = cat;
1454  }
1455 
1456  /* update offset array for nodes */
1457  if (is_node) {
1458  int node;
1459 
1460  struct Format_info_offset *offset;
1461 
1462  offset = &(pg_info->offset);
1463 
1464  node = abs(line);
1465  if (node > offset->array_alloc) {
1466  offset->array_alloc += 1000;
1467  offset->array = (int *) G_realloc (offset->array, offset->array_alloc * sizeof(int));
1468  }
1469 
1470  offset->array_num = node;
1471  offset->array[node-1] = (int) line_id; /* node id starts at 1 */
1472  }
1473 
1474  /* update PostGIS-line topo */
1475  if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
1476  update_topo_face(Map, line); /* TODO: avoid extra statements */
1477 
1478  return !is_node ? line : 0;
1479 }
1480 
1481 /*!
1482  \brief Binary data to HEX
1483 
1484  Allocated buffer should be freed by G_free().
1485 
1486  \param nbytes number of bytes to allocate
1487  \param wkb_data WKB data
1488 
1489  \return allocated buffer with HEX data
1490  */
1491 char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
1492 {
1493  char *hex_data;
1494  int i, nlow, nhigh;
1495  static const char ach_hex[] = "0123456789ABCDEF";
1496 
1497  hex_data = (char *)G_malloc(nbytes * 2 + 1);
1498  hex_data[nbytes * 2] = '\0';
1499 
1500  for (i = 0; i < nbytes; i++) {
1501  nlow = wkb_data[i] & 0x0f;
1502  nhigh = (wkb_data[i] & 0xf0) >> 4;
1503 
1504  hex_data[i * 2] = ach_hex[nhigh];
1505  hex_data[i * 2 + 1] = ach_hex[nlow];
1506  }
1507 
1508  return hex_data;
1509 }
1510 
1511 /*!
1512  \brief Write point into WKB buffer
1513 
1514  See OGRPoint::exportToWkb from GDAL/OGR library
1515 
1516  \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
1517  \param points feature geometry
1518  \param with_z WITH_Z for 3D data
1519  \param[out] nsize buffer size
1520 
1521  \return allocated WKB buffer
1522  \return NULL on error
1523  */
1524 unsigned char *point_to_wkb(int byte_order,
1525  const struct line_pnts *points, int with_z,
1526  int *nsize)
1527 {
1528  unsigned char *wkb_data;
1529  unsigned int sf_type;
1530 
1531  if (points->n_points != 1)
1532  return NULL;
1533 
1534  /* allocate buffer */
1535  *nsize = with_z ? 29 : 21;
1536  wkb_data = G_malloc(*nsize);
1537  G_zero(wkb_data, *nsize);
1538 
1539  G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
1540 
1541  /* set the byte order */
1542  if (byte_order == ENDIAN_LITTLE)
1543  wkb_data[0] = '\001';
1544  else
1545  wkb_data[0] = '\000';
1546 
1547  /* set the geometry feature type */
1548  sf_type = with_z ? SF_POINT25D : SF_POINT;
1549 
1550  if (byte_order == ENDIAN_LITTLE)
1551  sf_type = LSBWORD32(sf_type);
1552  else
1553  sf_type = MSBWORD32(sf_type);
1554  memcpy(wkb_data + 1, &sf_type, 4);
1555 
1556  /* copy in the raw data */
1557  memcpy(wkb_data + 5, &(points->x[0]), 8);
1558  memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
1559 
1560  if (with_z) {
1561  memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
1562  }
1563 
1564  /* swap if needed */
1565  if (byte_order == ENDIAN_BIG) {
1566  SWAPDOUBLE(wkb_data + 5);
1567  SWAPDOUBLE(wkb_data + 5 + 8);
1568 
1569  if (with_z)
1570  SWAPDOUBLE(wkb_data + 5 + 16);
1571  }
1572 
1573  return wkb_data;
1574 }
1575 
1576 /*!
1577  \bried Write linestring into WKB buffer
1578 
1579  See OGRLineString::exportToWkb from GDAL/OGR library
1580 
1581  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1582  \param points feature geometry
1583  \param with_z WITH_Z for 3D data
1584  \param[out] nsize buffer size
1585 
1586  \return allocated WKB buffer
1587  \return NULL on error
1588  */
1589 unsigned char *linestring_to_wkb(int byte_order,
1590  const struct line_pnts *points, int with_z,
1591  int *nsize)
1592 {
1593  int i, point_size;
1594  unsigned char *wkb_data;
1595  unsigned int sf_type;
1596 
1597  if (points->n_points < 1)
1598  return NULL;
1599 
1600  /* allocate buffer */
1601  point_size = 8 * (with_z ? 3 : 2);
1602  *nsize = 5 + 4 + points->n_points * point_size;
1603  wkb_data = G_malloc(*nsize);
1604  G_zero(wkb_data, *nsize);
1605 
1606  G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
1607 
1608  /* set the byte order */
1609  if (byte_order == ENDIAN_LITTLE)
1610  wkb_data[0] = '\001';
1611  else
1612  wkb_data[0] = '\000';
1613 
1614  /* set the geometry feature type */
1615  sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
1616 
1617  if (byte_order == ENDIAN_LITTLE)
1618  sf_type = LSBWORD32(sf_type);
1619  else
1620  sf_type = MSBWORD32(sf_type);
1621  memcpy(wkb_data + 1, &sf_type, 4);
1622 
1623  /* copy in the data count */
1624  memcpy(wkb_data + 5, &(points->n_points), 4);
1625 
1626  /* copy in the raw data */
1627  for (i = 0; i < points->n_points; i++) {
1628  memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
1629  memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
1630 
1631  if (with_z) {
1632  memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
1633  }
1634  }
1635 
1636  /* swap if needed */
1637  if (byte_order == ENDIAN_BIG) {
1638  int npoints, nitems;
1639 
1640  npoints = SWAP32(points->n_points);
1641  memcpy(wkb_data + 5, &npoints, 4);
1642 
1643  nitems = (with_z ? 3 : 2) * points->n_points;
1644  for (i = 0; i < nitems; i++) {
1645  SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
1646  }
1647  }
1648 
1649  return wkb_data;
1650 }
1651 
1652 /*!
1653  \bried Write polygon into WKB buffer
1654 
1655  See OGRPolygon::exportToWkb from GDAL/OGR library
1656 
1657  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1658  \param ipoints list of ring geometries (first is outer ring)
1659  \param nrings number of rings
1660  \param with_z WITH_Z for 3D data
1661  \param[out] nsize buffer size
1662 
1663  \return allocated WKB buffer
1664  \return NULL on error
1665  */
1666 unsigned char *polygon_to_wkb(int byte_order,
1667  const struct line_pnts** points, int nrings,
1668  int with_z, int *nsize)
1669 {
1670  int i, ring, point_size, offset;
1671  unsigned char *wkb_data;
1672  unsigned int sf_type;
1673 
1674  /* check data validity */
1675  if (nrings < 1)
1676  return NULL;
1677  for (ring = 0; ring < nrings; ring++) {
1678  if (points[ring]->n_points < 3)
1679  return NULL;
1680  }
1681 
1682  /* allocate buffer */
1683  point_size = 8 * (with_z ? 3 : 2);
1684  *nsize = 9;
1685  for (ring = 0; ring < nrings; ring++)
1686  *nsize += 4 + point_size * points[ring]->n_points;
1687  wkb_data = G_malloc(*nsize);
1688  G_zero(wkb_data, *nsize);
1689 
1690  G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
1691 
1692  /* set the byte order */
1693  if (byte_order == ENDIAN_LITTLE)
1694  wkb_data[0] = '\001';
1695  else
1696  wkb_data[0] = '\000';
1697 
1698  /* set the geometry feature type */
1699  sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
1700 
1701  if (byte_order == ENDIAN_LITTLE)
1702  sf_type = LSBWORD32(sf_type);
1703  else
1704  sf_type = MSBWORD32(sf_type);
1705  memcpy(wkb_data + 1, &sf_type, 4);
1706 
1707  /* copy in the raw data */
1708  if (byte_order == ENDIAN_BIG) {
1709  int ncount;
1710 
1711  ncount = SWAP32(nrings);
1712  memcpy(wkb_data + 5, &ncount, 4);
1713  }
1714  else {
1715  memcpy(wkb_data + 5, &nrings, 4);
1716  }
1717 
1718  /* serialize rings */
1719  offset = 9;
1720  for (ring = 0; ring < nrings; ring++) {
1721  memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
1722  for (i = 0; i < points[ring]->n_points; i++) {
1723  memcpy(wkb_data + offset +
1724  4 + point_size * i, &(points[ring]->x[i]), 8);
1725  memcpy(wkb_data + offset +
1726  4 + 8 + point_size * i, &(points[ring]->y[i]), 8);
1727 
1728  if (with_z) {
1729  memcpy(wkb_data + offset +
1730  4 + 16 + point_size * i, &(points[ring]->z[i]), 8);
1731  }
1732  }
1733 
1734  offset += 4 + point_size * points[ring]->n_points;
1735 
1736  /* swap if needed */
1737  if (byte_order == ENDIAN_BIG) {
1738  int npoints, nitems;
1739 
1740  npoints = SWAP32(points[ring]->n_points);
1741  memcpy(wkb_data + 5, &npoints, 4);
1742 
1743  nitems = (with_z ? 3 : 2) * points[ring]->n_points;
1744  for (i = 0; i < nitems; i++) {
1745  SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
1746  }
1747  }
1748  }
1749 
1750  return wkb_data;
1751 }
1752 
1753 /*!
1754  \brief Write feature to WKB buffer
1755 
1756  Allocated string buffer should be freed by G_free().
1757 
1758  \param pg_info pointer to Format_info_pg struct
1759  \param points array of geometries which form feature
1760  \param nparts number of geometries in array
1761  \param type feature type (GV_POINT, GV_LINE, ...)
1762  \param with_z WITH_Z for 3D data
1763 
1764  \return allocated string buffer
1765  \return NULL on error
1766 */
1767 char *line_to_wkb(struct Format_info_pg *pg_info,
1768  const struct line_pnts **points, int nparts, int type, int with_z)
1769 {
1770  int byte_order, nbytes, nsize;
1771  unsigned int sf_type;
1772 
1773  unsigned char *wkb_data;
1774  char *text_data, *text_data_p, *hex_data;
1775 
1776  byte_order = dig__byte_order_out();
1777 
1778  /* get wkb data */
1779  nbytes = -1;
1780  wkb_data = NULL;
1781  if (type & GV_POINTS) /* point or centroid */
1782  wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
1783  else if (type == GV_LINE ||
1784  (type == GV_BOUNDARY && pg_info->feature_type == SF_LINESTRING))
1785  wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1786  else if (type & (GV_BOUNDARY | GV_FACE | GV_AREA)) {
1787  if (!pg_info->toposchema_name || type == GV_AREA) {
1788  /* PostGIS simple feature access */
1789  wkb_data = polygon_to_wkb(byte_order, points, nparts,
1790  with_z, &nbytes);
1791  }
1792  else {
1793  /* PostGIS topology access */
1794  wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1795  }
1796  }
1797 
1798  if (!wkb_data || nbytes < 1) {
1799  G_warning(_("Unsupported feature type %d"), type);
1800  return NULL;
1801  }
1802 
1803  /* When converting to hex, each byte takes 2 hex characters. In
1804  addition we add in 8 characters to represent the SRID integer
1805  in hex, and one for a null terminator */
1806  nsize = nbytes * 2 + 8 + 1;
1807  text_data = text_data_p = (char *)G_malloc(nsize);
1808 
1809  /* convert the 1st byte, which is the endianess flag, to hex */
1810  hex_data = binary_to_hex(1, wkb_data);
1811  strcpy(text_data_p, hex_data);
1812  G_free(hex_data);
1813  text_data_p += 2;
1814 
1815  /* get the geom type which is bytes 2 through 5 */
1816  memcpy(&sf_type, wkb_data + 1, 4);
1817 
1818  /* add the SRID flag if an SRID is provided */
1819  if (pg_info->srid > 0) {
1820  unsigned int srs_flag;
1821 
1822  /* change the flag to little endianess */
1823  srs_flag = LSBWORD32(WKBSRIDFLAG);
1824  /* apply the flag */
1825  sf_type = sf_type | srs_flag;
1826  }
1827 
1828  /* write the geom type which is 4 bytes */
1829  hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
1830  strcpy(text_data_p, hex_data);
1831  G_free(hex_data);
1832  text_data_p += 8;
1833 
1834  /* include SRID if provided */
1835  if (pg_info->srid > 0) {
1836  unsigned int srs_id;
1837 
1838  /* force the srsid to little endianess */
1839  srs_id = LSBWORD32(pg_info->srid);
1840  hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
1841  strcpy(text_data_p, hex_data);
1842  G_free(hex_data);
1843  text_data_p += 8;
1844  }
1845 
1846  /* copy the rest of the data over - subtract 5 since we already
1847  copied 5 bytes above */
1848  hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
1849  strcpy(text_data_p, hex_data);
1850  G_free(hex_data);
1851 
1852  return text_data;
1853 }
1854 
1855 /*!
1856  \brief Insert feature into table
1857 
1858  \param Map pointer to Map_info structure
1859  \param line feature id (topo access only)
1860  \param type feature type (GV_POINT, GV_LINE, ...)
1861  \param points pointer to line_pnts struct
1862  \param nparts number of parts (rings for polygon)
1863  \param cat category number (-1 for no category)
1864 
1865  \return topo_id for PostGIS Topology
1866  \return 0 for simple features access
1867  \return -1 on error
1868  */
1869 int write_feature(struct Map_info *Map, int line, int type,
1870  const struct line_pnts **points, int nparts, int cat)
1871 {
1872  int with_z, topo_id;
1873  char *stmt, *geom_data;
1874 
1875  struct Format_info_pg *pg_info;
1876 
1877  pg_info = &(Map->fInfo.pg);
1878  with_z = Map->head.with_z;
1879 
1880  if (with_z && pg_info->coor_dim != 3) {
1881  G_warning(_("Trying to insert 3D data into feature table "
1882  "which store 2D data only"));
1883  return -1;
1884  }
1885  if (!with_z && pg_info->coor_dim != 2) {
1886  G_warning(_("Trying to insert 2D data into feature table "
1887  "which store 3D data only"));
1888  return -1;
1889  }
1890 
1891  /* build WKB geometry from line_pnts structures */
1892  geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
1893  if (!geom_data)
1894  return -1;
1895 
1896  /* start transaction */
1897  if (!pg_info->inTransaction) {
1898  pg_info->inTransaction = TRUE;
1899  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
1900  G_free(geom_data);
1901  return -1;
1902  }
1903  }
1904 
1905  /* write feature in PostGIS topology schema if enabled */
1906  topo_id = -1;
1907  if (pg_info->toposchema_name) {
1908  /* insert feature into topology schema (node or edge) */
1909  topo_id = insert_topo_element(Map, line, type, geom_data);
1910  if (topo_id < 0) {
1911  G_warning(_("Unable to insert topological element into PostGIS Topology schema"));
1912  G_free(geom_data);
1913 
1914  return -1;
1915  }
1916 
1917  if (pg_info->feature_type != SF_POLYGON) {
1918  /* define relation */
1919  Vect__define_topo_relation(pg_info, topo_id, topo_id);
1920  }
1921  }
1922 
1923  /* build INSERT statement
1924  simple feature geometry + attributes
1925  */
1926  stmt = build_insert_stmt(pg_info, geom_data, topo_id, cat);
1927 
1928  /* stmt can NULL when writing PostGIS topology with no attributes
1929  * attached */
1930  if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
1931  /* rollback transaction */
1932  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1933  G_free(geom_data);
1934  G_free(stmt);
1935 
1936  return -1;
1937  }
1938  G_free(geom_data);
1939  G_free(stmt);
1940 
1941  return pg_info->toposchema_name ? topo_id : 0;
1942 }
1943 
1944 /*!
1945  \brief Build INSERT statement to add new feature to the feature
1946  table
1947 
1948  Note: Allocated string should be freed.
1949 
1950  \param pg_info pointer to Format_info_pg structure
1951  \param geom_data geometry data
1952  \param type feature type (GV_POINT, GV_LINE, ...) - (only for PostGIS Topology)
1953  \param id topology element id (only for PostGIS Topology)
1954  \param cat category number (or -1 for no category)
1955  \param Fi pointer to field_info structure (NULL for no attributes)
1956 
1957  \return allocated string with INSERT statement
1958  */
1959 char *build_insert_stmt(const struct Format_info_pg *pg_info,
1960  const char *geom_data, int topo_id, int cat)
1961 {
1962  int topogeom_type;
1963 
1964  char *stmt, buf[DB_SQL_MAX];
1965 
1966  struct field_info *Fi;
1967 
1968  topogeom_type = -1;
1969  if (pg_info->toposchema_name) {
1970  topogeom_type = type_to_topogeom(pg_info);
1971  if (topogeom_type < 0)
1972  return NULL;
1973  }
1974 
1975  Fi = pg_info->fi;
1976 
1977  stmt = NULL;
1978  if (Fi && cat > -1) {
1979  /* write attributes (simple features and topology elements) */
1980  int col, ncol, more;
1981  int sqltype, ctype, is_fid;
1982  char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
1983 
1984  const char *colname;
1985 
1986  dbString dbstmt;
1987  dbCursor cursor;
1988  dbTable *table;
1989  dbColumn *column;
1990  dbValue *value;
1991 
1992  db_init_string(&dbstmt);
1993  buf_val[0] = '\0';
1994 
1995  /* read & set attributes */
1996  sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
1997  cat);
1998  G_debug(4, "SQL: %s", buf);
1999  db_set_string(&dbstmt, buf);
2000 
2001  /* prepare INSERT statement */
2002  sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
2003  pg_info->schema_name, pg_info->table_name);
2004 
2005  /* select data */
2006  if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
2007  &cursor, DB_SEQUENTIAL) != DB_OK) {
2008  G_warning(_("Unable to select attributes for category %d"), cat);
2009  }
2010  else {
2011  if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
2012  G_warning(_("Unable to fetch data from table <%s>"),
2013  Fi->table);
2014  }
2015 
2016  if (!more) {
2017  G_warning(_("No database record for category %d, "
2018  "no attributes will be written"), cat);
2019  }
2020  else {
2021  table = db_get_cursor_table(&cursor);
2022  ncol = db_get_table_number_of_columns(table);
2023 
2024  for (col = 0; col < ncol; col++) {
2025  column = db_get_table_column(table, col);
2026  colname = db_get_column_name(column);
2027 
2028  /* -> values */
2029  value = db_get_column_value(column);
2030  /* for debug only */
2031  db_convert_column_value_to_string(column, &dbstmt);
2032  G_debug(3, "col %d : val = %s", col,
2033  db_get_string(&dbstmt));
2034 
2035  sqltype = db_get_column_sqltype(column);
2036  ctype = db_sqltype_to_Ctype(sqltype);
2037 
2038  is_fid = strcmp(pg_info->fid_column, colname) == 0;
2039 
2040  /* check fid column (must be integer) */
2041  if (is_fid == TRUE &&
2042  ctype != DB_C_TYPE_INT) {
2043  G_warning(_("FID column must be integer, column <%s> ignored!"),
2044  colname);
2045  continue;
2046  }
2047 
2048  /* -> columns */
2049  sprintf(buf_tmp, "\"%s\"", colname);
2050  strcat(buf, buf_tmp);
2051  if (col < ncol - 1)
2052  strcat(buf, ",");
2053 
2054  /* prevent writing NULL values */
2055  if (!db_test_value_isnull(value)) {
2056  switch (ctype) {
2057  case DB_C_TYPE_INT:
2058  sprintf(buf_tmp, "%d", db_get_value_int(value));
2059  break;
2060  case DB_C_TYPE_DOUBLE:
2061  sprintf(buf_tmp, "%.14f",
2062  db_get_value_double(value));
2063  break;
2064  case DB_C_TYPE_STRING: {
2065  char *value_tmp;
2066  value_tmp = G_str_replace(db_get_value_string(value), "'", "''");
2067  sprintf(buf_tmp, "'%s'", value_tmp);
2068  G_free(value_tmp);
2069  break;
2070  }
2071  case DB_C_TYPE_DATETIME:
2073  &dbstmt);
2074  sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
2075  break;
2076  default:
2077  G_warning(_("Unsupported column type %d"), ctype);
2078  sprintf(buf_tmp, "NULL");
2079  break;
2080  }
2081  }
2082  else {
2083  if (is_fid == TRUE)
2084  G_warning(_("Invalid value for FID column: NULL"));
2085  sprintf(buf_tmp, "NULL");
2086  }
2087  strcat(buf_val, buf_tmp);
2088  if (col < ncol - 1)
2089  strcat(buf_val, ",");
2090  }
2091 
2092  if (!pg_info->toposchema_name) {
2093  /* simple feature access */
2094  G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
2095  buf, pg_info->geom_column, buf_val, geom_data);
2096  }
2097  else {
2098  /* PostGIS topology access, write geometry in
2099  * topology schema, skip geometry at this point */
2100  if (buf[strlen(buf)-1] == ',') { /* last column skipped */
2101  buf[strlen(buf)-1] = '\0';
2102  buf_val[strlen(buf_val)-1] = '\0';
2103  }
2104  G_asprintf(&stmt, "%s, %s) VALUES (%s, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
2105  buf, pg_info->topogeom_column, buf_val,
2106  pg_info->toposchema_id, topo_id, topogeom_type);
2107  }
2108  }
2109  }
2110  }
2111  else {
2112  /* no attributes */
2113  if (!pg_info->toposchema_name) {
2114  /* no attributes (simple features access) */
2115  if (cat > 0) {
2116  /* cetegory defined */
2117  G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2118  "(%d, '%s'::GEOMETRY)",
2119  pg_info->schema_name, pg_info->table_name,
2120  GV_KEY_COLUMN, pg_info->geom_column,
2121  cat, geom_data);
2122  }
2123  else {
2124  /* no category */
2125  G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
2126  "('%s'::GEOMETRY)",
2127  pg_info->schema_name, pg_info->table_name,
2128  pg_info->geom_column, geom_data);
2129  }
2130  }
2131  else {
2132  if (cat > 0) {
2133  /* no attributes (topology elements) */
2134  G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2135  "(%d, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
2136  pg_info->schema_name, pg_info->table_name,
2137  GV_KEY_COLUMN, pg_info->topogeom_column, cat,
2138  pg_info->toposchema_id, topo_id, topogeom_type);
2139  }
2140  }
2141  }
2142 
2143  return stmt;
2144 }
2145 
2146 /*!
2147  \brief Insert topological element into 'node' or 'edge' table
2148 
2149  Negative id for nodes, 0 for next value.
2150 
2151  \param Map pointer to Map_info struct
2152  \param id feature id (0 for next val)
2153  \param type feature type (GV_POINT, GV_LINE, ...)
2154  \param geom_data geometry in wkb
2155 
2156  \return new topo id
2157  \return -1 on error
2158 */
2159 int insert_topo_element(struct Map_info *Map, int id, int type,
2160  const char *geom_data)
2161 {
2162  int ret, topo_id;
2163  char *stmt, stmt_id[DB_SQL_MAX];
2164  struct Format_info_pg *pg_info;
2165  struct Plus_head *plus;
2166  struct P_line *Line;
2167 
2168  pg_info = &(Map->fInfo.pg);
2169  plus = &(Map->plus);
2170 
2171  Line = NULL;
2172  if (plus->built >= GV_BUILD_BASE) {
2173  if (id > 0) { /* -> feature */
2174  topo_id = id;
2175  if (topo_id > Map->plus.n_lines) {
2176  G_warning(_("Invalid feature %d (max: %d)"), topo_id, Map->plus.n_lines);
2177  return -1;
2178  }
2179  Line = Map->plus.Line[topo_id];
2180 
2181  if (Line->type & GV_POINTS) {
2182  /* set topo_id for points */
2183  topo_id = Vect_get_num_primitives(Map, GV_POINTS) + Vect_get_num_nodes(Map);
2184  }
2185 
2186  }
2187  else if (id < 0) { /* node */
2188  topo_id = abs(id);
2189  if (type != GV_POINT) {
2190  G_warning(_("Invalid feature type (%d) for node"), type);
2191  return -1;
2192  }
2193  if (topo_id > Map->plus.n_nodes) {
2194  G_warning(_("Invalid node %d (%d)"), topo_id, Map->plus.n_nodes);
2195  return -1;
2196  }
2197 
2198  /* increment topo_id - also points and centroids are
2199  * stored in 'node' table */
2200  topo_id += Vect_get_num_primitives(Map, GV_POINTS);
2201  }
2202  }
2203 
2204  stmt = NULL;
2205  switch(type) {
2206  case GV_POINT: {
2207  /* insert new node */
2208 #if USE_TOPO_STMT
2209  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2210  pg_info->toposchema_name, geom_data);
2211 #else
2212  if (id == 0) {
2213  /* get node_id */
2214  sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
2215  pg_info->toposchema_name);
2216  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2217  }
2218 
2219  /* build insert statement */
2220  G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, geom) VALUES "
2221  "(%d, '%s'::GEOMETRY)", pg_info->toposchema_name, topo_id, geom_data);
2222 #endif
2223  break;
2224  }
2225  case GV_LINE:
2226  case GV_BOUNDARY: {
2227  /* insert new edge */
2228 #if USE_TOPO_STMT
2229  G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
2230  pg_info->toposchema_name, geom_data);
2231 #else
2232  int n1, n2, nle, nre;
2233 
2234  struct Format_info_offset *offset;
2235 
2236  offset = &(pg_info->offset);
2237 
2238  if (id == 0) {
2239  /* get edge_id */
2240  sprintf(stmt_id, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
2241  pg_info->toposchema_name);
2242  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2243  }
2244 
2245  nle = -topo_id; /* assuming isolated lines */
2246  nre = topo_id;
2247 
2248  if (Line) {
2249  int i, n, next_edge;
2250  struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
2251 
2252  topo_id = (int)Line->offset;
2253  /* start & end node */
2254  n1 = topo->N1;
2255  n2 = topo->N2;
2256 
2257  /* next left & right edge */
2258  for (i = 0; i < 2; i++) {
2259  n = Vect_get_node_n_lines(Map, i == 0 ? n1 : n2);
2260  if (n < 2) /* no connection */
2261  continue;
2262 
2263  next_edge = update_next_edge(Map, n,
2264  i == 0 ? topo_id : -topo_id);
2265  if (next_edge != 0) {
2266  if (i == 0)
2267  nre = next_edge; /* update next right edge for start node */
2268  else
2269  nle = next_edge; /* update next left edge for end node */
2270  }
2271  else {
2272  G_warning(_("Unable to determine next left/right edge for edge %d"), topo_id);
2273  }
2274  }
2275  }
2276  else {
2277  G_warning(_("Unable to insert new edge. Topology not available."));
2278  return -1;
2279  }
2280 
2281  G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
2282  topo_id, nle, nre);
2283 
2284  if (n1 > offset->array_num || n2 > offset->array_num) /* node id starts at 1 */
2285  return -1;
2286 
2287  /* build insert statement */
2288  G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
2289  "next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
2290  "left_face, right_face, geom) VALUES "
2291  "(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
2292  pg_info->toposchema_name, topo_id, offset->array[n1-1], offset->array[n2-1],
2293  nle, abs(nle), nre, abs(nre), geom_data);
2294 #endif
2295  break;
2296  }
2297  case GV_CENTROID: {
2298  /* insert new node (with containing_face) */
2299 #if USE_TOPO_STMT
2300  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2301  pg_info->toposchema_name, geom_data);
2302 #else
2303  int area;
2304 
2305  if (id == 0) {
2306  /* get node_id */
2307  sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
2308  pg_info->toposchema_name);
2309  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2310  }
2311 
2312  if (Line) {
2313  struct P_topo_c *topo = (struct P_topo_c *) Line->topo;
2314 
2315  area = topo->area;
2316  }
2317  else {
2318  area = 0;
2319  }
2320 
2321  G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, containing_face, geom) VALUES "
2322  "(%d, %d, '%s'::GEOMETRY)", pg_info->toposchema_name,
2323  topo_id, area, geom_data);
2324 #endif
2325  break;
2326  }
2327  default:
2328  G_warning(_("Unsupported feature type %d"), type);
2329  break;
2330  }
2331 
2332  /* execute insert statement */
2333  ret = Vect__execute_pg(pg_info->conn, stmt);
2334  G_free(stmt);
2335 
2336  if (ret == -1) {
2337  /* rollback transaction */
2338  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2339  return -1;
2340  }
2341 
2342  return topo_id;
2343 }
2344 
2345 int type_to_topogeom(const struct Format_info_pg *pg_info)
2346 {
2347  int topogeom_type;
2348 
2349  topogeom_type = -1;
2350  switch (pg_info->feature_type) {
2351  case SF_POINT:
2352  topogeom_type = 1;
2353  break;
2354  case SF_LINESTRING:
2355  topogeom_type = 2;
2356  break;
2357  case SF_POLYGON:
2358  topogeom_type = 3;
2359  break;
2360  default:
2361  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
2362  }
2363 
2364  return topogeom_type;
2365 }
2366 
2367 int Vect__define_topo_relation(const struct Format_info_pg *pg_info, int topo_id, int element_id)
2368 {
2369  int topogeom_type;
2370  char stmt[DB_SQL_MAX];
2371 
2372  topogeom_type = type_to_topogeom(pg_info);
2373  if (topogeom_type < 0)
2374  return -1;
2375 
2376  sprintf(stmt, "INSERT into \"%s\".relation VALUES(%d, 1, %d, %d)",
2377  pg_info->toposchema_name, topo_id, element_id, topogeom_type);
2378  G_debug(3, "SQL: %s", stmt);
2379 
2380  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2381  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2382  return -1;
2383  }
2384 
2385  return 0;
2386 }
2387 
2388 /*!
2389  \brief Find next line (topo only)
2390 
2391  \param Map pointer to Map_info struct
2392  \param nlines number of lines
2393  \param line current line
2394  \param[out] left left line
2395  \param[out] right right line
2396 
2397  \return left (line < 0) or right (line > 0) next edge
2398  \return 0 on failure
2399 */
2400 int update_next_edge(struct Map_info* Map, int nlines, int line)
2401 {
2402  int ret, next_line, edge;
2403  char stmt[DB_SQL_MAX];
2404 
2405  const struct Format_info_pg *pg_info;
2406  struct P_line *Line_next, *Line;
2407 
2408  Line = Line_next = NULL;
2409 
2410  pg_info = &(Map->fInfo.pg);
2411 
2412  /* find next line
2413  start node -> next on the left
2414  end node -> next on the right
2415  */
2416  next_line = dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
2417  G_debug(3, "line=%d next_line=%d", line, next_line);
2418  if (next_line == 0) {
2419  G_warning(_("Invalid topology"));
2420  return 0;
2421  }
2422 
2423  Line = Map->plus.Line[abs(line)];
2424  Line_next = Map->plus.Line[abs(next_line)];
2425  if (!Line || !Line_next) {
2426  G_warning(_("Invalid topology"));
2427  return 0;
2428  }
2429 
2430  if (line > 0) {
2431  edge = Line->offset;
2432  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2433  }
2434  else {
2435  edge = -Line->offset;
2436  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2437  }
2438 
2439  if (next_line < 0) {
2440  sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2441  "abs_next_left_edge = %d WHERE edge_id = %d AND abs_next_left_edge = %d",
2442  pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
2443  G_debug(3, "update edge=%d next_left_edge=%d (?)", (int)Line_next->offset, edge);
2444  }
2445  else {
2446  sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2447  "abs_next_right_edge = %d WHERE edge_id = %d AND abs_next_right_edge = %d",
2448  pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
2449  G_debug(3, "update edge=%d next_right_edge=%d (?)", (int)Line_next->offset, edge);
2450  }
2451 
2452  if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
2453  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2454  return 0;
2455  }
2456 
2457  if (nlines > 2) {
2458  /* more lines connected to the node
2459 
2460  start node -> next on the right
2461  end node -> next on the left
2462  */
2463  next_line = dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
2464  Line_next = Map->plus.Line[abs(next_line)];
2465 
2466  if (next_line < 0) {
2467  sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2468  "abs_next_left_edge = %d WHERE edge_id = %d",
2469  pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
2470  G_debug(3, "update edge=%d next_left_edge=%d", (int)Line_next->offset, edge);
2471  }
2472  else {
2473  sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2474  "abs_next_right_edge = %d WHERE edge_id = %d",
2475  pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
2476  G_debug(3, "update edge=%d next_right_edge=%d", (int)Line_next->offset, edge);
2477  }
2478 
2479  if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
2480  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2481  return 0;
2482  }
2483  }
2484 
2485  return ret;
2486 }
2487 
2488 /*!
2489  \brief Insert new face to the 'face' table (topo only)
2490 
2491  \param Map pointer to Map_info struct
2492  \param area area id (negative id for isles)
2493 
2494  \return 0 on error
2495  \return area id on success (>0)
2496 */
2497 int Vect__insert_face_pg(struct Map_info *Map, int area)
2498 {
2499  char *stmt;
2500 
2501  struct Format_info_pg *pg_info;
2502  struct bound_box box;
2503 
2504  if (area == 0)
2505  return 0; /* universal face has id '0' in PostGIS Topology */
2506 
2507  stmt = NULL;
2508  pg_info = &(Map->fInfo.pg);
2509 
2510  /* check if face exists */
2511 
2512  /* get mbr of the area */
2513  if (area > 0)
2514  Vect_get_area_box(Map, area, &box);
2515  else
2516  Vect_get_isle_box(Map, abs(area), &box);
2517 
2518  /* insert face if not exists */
2519  G_asprintf(&stmt, "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
2520  "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f, "
2521  "%.12f %.12f))', %d))", pg_info->toposchema_name, area,
2522  box.W, box.S, box.W, box.N, box.E, box.N,
2523  box.E, box.S, box.W, box.S, pg_info->srid);
2524  G_debug(3, "new face id=%d", area);
2525  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2526  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2527  return 0;
2528  }
2529  G_free(stmt);
2530 
2531  return area;
2532 }
2533 
2534 /*!
2535  \brief Delete existing face (currently unused)
2536 
2537  \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
2538  CONSTRAINTS ALL DEFERRED
2539 
2540  \param Map pointer to Map_info struct
2541  \param area area id to delete
2542 
2543  \return 0 on success
2544  \return -1 on error
2545 */
2546 int delete_face(const struct Map_info *Map, int area)
2547 {
2548  char stmt[DB_SQL_MAX];
2549 
2550  const struct Format_info_pg *pg_info;
2551 
2552  pg_info = &(Map->fInfo.pg);
2553 
2554  /* update centroids first */
2555  sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 "
2556  "WHERE containing_face = %d",
2557  pg_info->toposchema_name, area);
2558  G_debug(3, "SQL: %s", stmt);
2559  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2560  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2561  return -1;
2562  }
2563 
2564  /* update also edges (left face) */
2565  sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0 "
2566  "WHERE left_face = %d",
2567  pg_info->toposchema_name, area);
2568  G_debug(3, "SQL: %s", stmt);
2569  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2570  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2571  return -1;
2572  }
2573 
2574  /* update also edges (left face) */
2575  sprintf(stmt, "UPDATE \"%s\".edge_data SET right_face = 0 "
2576  "WHERE right_face = %d",
2577  pg_info->toposchema_name, area);
2578  G_debug(3, "SQL: %s", stmt);
2579  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2580  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2581  return -1;
2582  }
2583 
2584  /* delete face */
2585  sprintf(stmt, "DELETE FROM \"%s\".face WHERE face_id = %d",
2586  pg_info->toposchema_name, area);
2587  G_debug(3, "delete face id=%d", area);
2588  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2589  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2590  return -1;
2591  }
2592 
2593  return 0;
2594 }
2595 
2596 /*!
2597  \brief Update lines (next left and right edges)
2598 
2599  - isolated edges
2600  next left edge: -edge
2601  next right edge: edge
2602 
2603  - connected edges
2604  next left edge: next edge or -edge
2605  next right edge: next edge or edge
2606 
2607  \param Map pointer to Map_info struct
2608  \param line feature id
2609 
2610  \return 0 on success
2611  \return -1 on error
2612 */
2613 int update_topo_edge(struct Map_info *Map, int line)
2614 {
2615  int i, n;
2616  int nle, nre, next_edge;
2617  char stmt[DB_SQL_MAX];
2618 
2619  struct Format_info_pg *pg_info;
2620  struct P_line *Line;
2621 
2622  pg_info = &(Map->fInfo.pg);
2623 
2624  if (line < 1 || line > Map->plus.n_lines) {
2625  G_warning(_("Attempt to access non-existing feature %d"), line);
2626  return -1;
2627  }
2628  Line = Map->plus.Line[line];
2629  if (!Line) {
2630  G_warning(_("Attempt to access dead feature %d"), line);
2631  return -1;
2632  }
2633 
2634  struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
2635 
2636  nre = nle = 0; /* edge = 0 is an illegal value */
2637 
2638  /* check for line connection */
2639  for (i = 0; i < 2; i++) {
2640  /* first check start node then end node */
2641  n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
2642  : Vect_get_node_n_lines(Map, topo->N2);
2643 
2644  if (n < 2) /* no connection */
2645  continue;
2646 
2647  next_edge = update_next_edge(Map, n,
2648  i == 0 ? line : -line);
2649  if (next_edge != 0) {
2650  if (i == 0)
2651  nre = next_edge; /* update next right edge for start node */
2652  else
2653  nle = next_edge; /* update next left edge for end node */
2654  }
2655  else {
2656  G_warning(_("Unable to determine next left/right edge"));
2657  return -1;
2658  }
2659  }
2660 
2661  if (nle == 0 && nre == 0) /* nothing changed */
2662  return 0;
2663 
2664  if (nle != 0 && nre != 0) {
2665  /* update both next left and right edge */
2666  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2667  "next_left_edge = %d, abs_next_left_edge = %d, "
2668  "next_right_edge = %d, abs_next_right_edge = %d "
2669  "WHERE edge_id = %d", pg_info->toposchema_name,
2670  nle, abs(nle), nre, abs(nre), (int)Line->offset);
2671  }
2672  else if (nle != 0) {
2673  /* update next left edge only */
2674  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2675  "next_left_edge = %d, abs_next_left_edge = %d "
2676  "WHERE edge_id = %d", pg_info->toposchema_name,
2677  nle, abs(nle), (int)Line->offset);
2678  }
2679  else {
2680  /* update next right edge only */
2681  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2682  "next_right_edge = %d, abs_next_right_edge = %d "
2683  "WHERE edge_id = %d", pg_info->toposchema_name,
2684  nre, abs(nre), (int)Line->offset);
2685  }
2686  G_debug(3, "update edge=%d next_left_edge=%d next_right_edge=%d",
2687  (int)Line->offset, nle, nre);
2688 
2689  if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
2690  /* rollback transaction */
2691  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2692  return -1;
2693  }
2694 
2695  return 0;
2696 }
2697 
2698 /*!
2699  \brief Update lines (left and right faces)
2700 
2701  TODO: handle isles
2702 
2703  \param Map pointer to Map_info struct
2704  \param line feature id
2705 
2706  \return 0 on success
2707  \return -1 on error
2708 */
2709 int update_topo_face(struct Map_info *Map, int line)
2710 {
2711  int i, s, area, face[2];
2712  char stmt[DB_SQL_MAX];
2713 
2714  struct Format_info_pg *pg_info;
2715  struct P_line *Line, *Line_i;
2716  struct P_area *Area;
2717  struct P_topo_b *topo, *topo_i;
2718 
2719  pg_info = &(Map->fInfo.pg);
2720 
2721  if (line < 1 || line > Map->plus.n_lines) {
2722  G_warning(_("Attempt to access non-existing feature %d"), line);
2723  return -1;
2724  }
2725  Line = Map->plus.Line[line];
2726  if (!Line) {
2727  G_warning(_("Attempt to access dead feature %d"), line);
2728  return -1;
2729  }
2730 
2731  topo = (struct P_topo_b *)Line->topo;
2732 
2733  /* for both side on the current boundary (line) */
2734  /* create new faces */
2735  for (s = 0; s < 2; s++) { /* for each side */
2736  area = s == 0 ? topo->left : topo->right;
2737  if (area <= 0) /* no area - skip */
2738  continue;
2739 
2740  face[s] = Vect__insert_face_pg(Map, area);
2741  if (face[s] < 1) {
2742  G_warning(_("Unable to create new face"));
2743  return -1;
2744  }
2745  }
2746 
2747  /* update edges forming faces */
2748  for (s = 0; s < 2; s++) { /* for each side */
2749  area = s == 0 ? topo->left : topo->right;
2750  if (area <= 0) /* no area - skip */
2751  continue;
2752 
2753  Area = Map->plus.Area[area];
2754  for (i = 0; i < Area->n_lines; i++) {
2755  Line_i = Map->plus.Line[abs(Area->lines[i])];
2756  topo_i = (struct P_topo_b *)Line_i->topo;
2757 
2758  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2759  "left_face = %d, right_face = %d "
2760  "WHERE edge_id = %d", pg_info->toposchema_name,
2761  topo_i->left > 0 ? topo_i->left : 0,
2762  topo_i->right > 0 ? topo_i->right : 0,
2763  (int) Line_i->offset);
2764  G_debug(2, "SQL: %s", stmt);
2765 
2766  if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
2767  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2768  return -1;
2769  }
2770  }
2771 
2772  /* update also centroids (stored as nodes) */
2773  if (Area->centroid > 0) {
2774  Line_i = Map->plus.Line[Area->centroid];
2775  sprintf(stmt, "UPDATE \"%s\".node SET containing_face = %d "
2776  "WHERE node_id = %d", pg_info->toposchema_name,
2777  face[s], (int)Line_i->offset);
2778  G_debug(2, "SQL: %s", stmt);
2779 
2780  if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
2781  /* rollback transaction */
2782  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2783  return -1;
2784  }
2785  }
2786  }
2787 
2788  return 0;
2789 }
2790 
2791 /*!
2792  \brief Add line to native and PostGIS topology
2793 
2794  \param Map vector map
2795  \param offset ???
2796  \param type feature type
2797  \param Points feature vertices
2798 
2799  \return feature id
2800  \return -1 on error
2801 */
2802 int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
2803  const struct line_pnts *points)
2804 {
2805  int line, n_nodes;
2806 
2807  struct Plus_head *plus;
2808 
2809  plus = &(Map->plus);
2810 
2811  Vect_reset_updated(Map);
2812  line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL,
2813  -1, NULL);
2814 
2815  /* insert new nodes into 'node' table */
2816  n_nodes = Vect_get_num_updated_nodes(Map);
2817  if (n_nodes > 0) {
2818  int i, node;
2819  double x, y, z;
2820 
2821  if (!Points)
2822  Points = Vect_new_line_struct();
2823 
2824  for (i = 0; i < n_nodes; i++) {
2825  node = Vect_get_updated_node(Map, i);
2826  /* skip updated and deleted nodes */
2827  if (node > 0 || plus->Node[abs(node)] == NULL)
2828  continue;
2829 
2830  G_debug(3, " new node: %d", node);
2831 
2832  Vect_get_node_coor(Map, abs(node), &x, &y, &z);
2833  Vect_reset_line(Points);
2834  Vect_append_point(Points, x, y, z);
2835 
2836  write_feature(Map, node, GV_POINT, (const struct line_pnts **) &Points, 1, -1);
2837  }
2838  }
2839 
2840  return line;
2841 }
2842 
2843 /*!
2844  \brief Delete line from native and PostGIS topology
2845 
2846  \param Map vector map
2847  \param line feature id to remove from topo
2848  \param type feature type
2849  \param Points feature vertices
2850 
2851  \return 0 on success
2852  \return -1 on error
2853 */
2854 int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
2855  const struct line_pnts *Points)
2856 {
2857  int N1, N2, node_id;
2858  char stmt[DB_SQL_MAX];
2859 
2860  struct Format_info_pg *pg_info;
2861  struct P_node *Node;
2862 
2863  pg_info = &(Map->fInfo.pg);
2864 
2865  Vect_reset_updated(Map);
2866 
2867  if (!(type & GV_LINES))
2868  return 0;
2869 
2870  Vect_get_line_nodes(Map, line, &N1, &N2);
2871  if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
2872  return -1;
2873 
2874  Node = Map->plus.Node[N1];
2875  if (!Node || Node->n_lines == 0) {
2876  node_id = pg_info->offset.array[N1-1];
2877  sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2878  pg_info->toposchema_name, node_id);
2879  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2880  G_warning(_("Unable to delete node %d"), node_id);
2881  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2882  return -1;
2883  }
2884  }
2885 
2886  Node = Map->plus.Node[N2];
2887  if (!Node || Node->n_lines == 0) {
2888  node_id = pg_info->offset.array[N2-1];
2889  sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2890  pg_info->toposchema_name, node_id);
2891  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2892  G_warning(_("Unable to delete node %d"), node_id);
2893  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2894  return -1;
2895  }
2896  }
2897 
2898  return 0;
2899 }
2900 
2901 int set_constraint_to_deferrable(struct Format_info_pg *pg_info,
2902  const char *table, const char *constraint,
2903  const char *column, const char *ref_table,
2904  const char *ref_column)
2905 {
2906  char stmt[DB_SQL_MAX];
2907 
2908  sprintf(stmt, "ALTER TABLE \"%s\".%s DROP CONSTRAINT %s",
2909  pg_info->toposchema_name, table, constraint);
2910  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
2911  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2912  return -1;
2913  }
2914 
2915  sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT %s "
2916  "FOREIGN KEY (%s) REFERENCES \"%s\".%s (%s) "
2917  "DEFERRABLE INITIALLY DEFERRED",
2918  pg_info->toposchema_name, table, constraint, column,
2919  pg_info->toposchema_name, ref_table, ref_column);
2920  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
2921  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2922  return -1;
2923  }
2924 
2925  return 0;
2926 }
2927 
2928 /*!
2929  \brief Open database connection with attribute table
2930 
2931  \param pg_info pointer to Format_info_pg struct
2932 
2933  \return pointer to dbDriver on succes
2934  \return NULL on failure
2935 */
2936 dbDriver *open_db(struct Format_info_pg *pg_info) {
2937  dbDriver *driver;
2938  dbHandle handle;
2939 
2940  struct field_info *Fi;
2941 
2942  db_init_handle(&handle);
2943 
2944  Fi = pg_info->fi;
2945 
2946  pg_info->dbdriver = driver = db_start_driver(Fi->driver);
2947  if (!driver) {
2948  G_warning(_("Unable to start driver <%s>"), Fi->driver);
2949  return NULL;
2950  }
2951  db_set_handle(&handle, Fi->database, NULL);
2952  if (db_open_database(driver, &handle) != DB_OK) {
2953  G_warning(_("Unable to open database <%s> by driver <%s>"),
2954  Fi->database, Fi->driver);
2956  pg_info->dbdriver = NULL;
2957  return NULL;
2958  }
2959 
2960  return pg_info->dbdriver;
2961 }
2962 #endif
int Vect_get_num_updated_nodes(const struct Map_info *Map)
Get number of updated nodes.
Definition: level_two.c:223
int * array
Offset list.
Definition: dig_structs.h:446
char * toposchema_name
Topology schema name and id.
Definition: dig_structs.h:699
#define TRUE
Definition: gis.h:56
#define ENDIAN_LITTLE
Endian check.
Definition: gis.h:377
#define GV_KEY_COLUMN
Name of default key column.
Definition: gis.h:385
int Vect_get_isle_box(const struct Map_info *Map, int isle, struct bound_box *Box)
Get bounding box of isle.
int db_test_value_isnull(dbValue *value)
Check of value is null.
Definition: value.c:26
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:46
const char * db_sqltype_name(int sqltype)
Get SQL data type description.
Definition: sqltype.c:25
Bounding box.
Definition: dig_structs.h:65
const char * G_find_key_value(const char *key, const struct Key_Value *kv)
Find given key (case sensitive)
Definition: key_value1.c:84
void G_free(void *buf)
Free allocated memory.
Definition: gis/alloc.c:149
int built
Highest level of topology currently available.
Definition: dig_structs.h:873
plus_t area
Area number, negative for duplicate centroid.
Definition: dig_structs.h:1537
off_t V2_write_line_pg(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature on topological level (PostGIS interface)
Definition: write_pg.c:142
#define GV_LEFT
Boundary side indicator left/right.
Definition: dig_defines.h:174
double W
West.
Definition: dig_structs.h:82
const char * db_get_column_name(dbColumn *column)
Returns column name for given column.
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1593
off_t V2_rewrite_line_pg(struct Map_info *Map, off_t line, int type, const struct line_pnts *points, const struct line_cats *cats)
Rewrites feature at topological level (PostGIS interface, internal use only)
Definition: write_pg.c:219
int Vect_get_node_coor(const struct Map_info *Map, int num, double *x, double *y, double *z)
Get node coordinates.
Definition: level_two.c:278
void G_verbose_message(const char *msg,...)
Print a message to stderr but only if module is in verbose mode.
Definition: gis/error.c:109
Vector geometry.
Definition: dig_structs.h:1574
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:887
int Vect_get_node_n_lines(const struct Map_info *Map, int node)
Get number of lines for node.
Definition: level_two.c:384
struct Key_Value * G_fread_key_value(FILE *fd)
Read key/values pairs from file.
Definition: key_value2.c:49
int db_close_database_shutdown_driver(dbDriver *driver)
Close driver/database connection.
Definition: db.c:62
int Vect_get_area_box(const struct Map_info *Map, int area, struct bound_box *Box)
Get bounding box of area.
SF_FeatureType feature_type
Feature type (simple feature access)
Definition: dig_structs.h:635
struct line_pnts * Vect_new_line_struct()
Creates and initializes a line_pnts structure.
Definition: line.c:45
int db_describe_table(dbDriver *driver, dbString *name, dbTable **table)
Describe table.
Definition: c_desc_table.c:28
plus_t right
Area number to the right, negative for isle.
Definition: dig_structs.h:1526
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:891
#define DB_SQL_MAX
Definition: dbmi.h:142
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:883
int n_points
Number of points.
Definition: dig_structs.h:1692
char * table
Name of DB table.
Definition: dig_structs.h:155
#define DB_C_TYPE_DATETIME
Definition: dbmi.h:110
#define GV_CENTROID
Definition: dig_defines.h:185
const char * Vect_get_finfo_geometry_type(const struct Map_info *Map)
Get geometry type as string (relevant only for non-native formats)
Definition: header_finfo.c:144
plus_t n_lines
Number of attached lines (size of lines, angle)
Definition: dig_structs.h:1472
void Vect_reset_updated(struct Map_info *Map)
Reset list of updated lines/nodes.
Definition: level_two.c:472
char * G_store(const char *s)
Copy string to allocated memory.
Definition: strings.c:86
plus_t left
Area number to the left, negative for isle.
Definition: dig_structs.h:1522
#define GV_POINTS
Definition: dig_defines.h:191
int * lines_cats
List of line cats (used only for PostGIS Topology access)
Definition: dig_structs.h:481
int db_get_column_length(dbColumn *column)
Get column&#39;s length.
void Vect_destroy_line_struct(struct line_pnts *p)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition: line.c:77
struct Format_info fInfo
Format info for non-native formats.
Definition: dig_structs.h:1415
double E
East.
Definition: dig_structs.h:78
#define PRI_OFF_T
Definition: gis.h:66
int Vect_get_num_dblinks(const struct Map_info *Map)
Get number of defined dblinks.
Definition: level_two.c:163
int G_asprintf(char **out, const char *fmt,...)
Definition: asprintf.c:70
int inTransaction
Start/Finish transaction.
Definition: dig_structs.h:658
plus_t centroid
Number of first centroid within area.
Definition: dig_structs.h:1628
int level
Topology level.
Definition: dig_structs.h:1313
char * table_name
Table name.
Definition: dig_structs.h:619
#define DB_SQL_TYPE_CHARACTER
Definition: dbmi.h:81
#define NULL
Definition: ccmath.h:32
dbDriver * dbdriver
Open DB driver when writing attributes.
Definition: dig_structs.h:652
int Vect_get_updated_node(const struct Map_info *Map, int idx)
Get updated (modified) node by index.
Definition: level_two.c:245
#define x
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:182
char * db_get_string(const dbString *x)
Get string.
Definition: string.c:140
char * topogeom_column
TopoGeometry column (feature table)
Definition: dig_structs.h:695
void G_free_key_value(struct Key_Value *kv)
Free allocated Key_Value structure.
Definition: key_value1.c:103
int V1_delete_line_pg(struct Map_info *Map, off_t offset)
Deletes feature at the given offset (level 1)
Definition: write_pg.c:311
plus_t N2
End node.
Definition: dig_structs.h:1503
char * database
Definition: dig_structs.h:151
int Vect_append_point(struct line_pnts *Points, double x, double y, double z)
Appends one point to the end of a line.
Definition: line.c:149
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:947
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160
int db_convert_column_value_to_string(dbColumn *column, dbString *string)
?
Definition: columnfmt.c:61
Feature category info.
Definition: dig_structs.h:1702
int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, int line)
Read feature from PostGIS layer on topological level.
Definition: read_pg.c:319
int db_sqltype_to_Ctype(int sqltype)
Get C data type based on given SQL data type.
Definition: sqlCtype.c:24
#define GV_LINE
Definition: dig_defines.h:183
double N
North.
Definition: dig_structs.h:70
plus_t N1
Start node.
Definition: dig_structs.h:1499
int db_fetch(dbCursor *cursor, int position, int *more)
Fetch data from open cursor.
Definition: c_fetch.c:28
Layer (old: field) information.
Definition: dig_structs.h:134
double * x
Array of X coordinates.
Definition: dig_structs.h:1680
int dig_angle_next_line(struct Plus_head *, plus_t, int, int, float *)
Find line number of next angle to follow a line.
Definition: plus_area.c:466
char type
Line type.
Definition: dig_structs.h:1586
int cidx_up_to_date
Category index to be updated.
Definition: dig_structs.h:1156
Feature geometry info - coordinates.
Definition: dig_structs.h:1675
int with_z
2D/3D vector data
Definition: dig_structs.h:347
Centroid topology.
Definition: dig_structs.h:1532
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1621
#define ENDIAN_BIG
Definition: gis.h:378
double db_get_value_double(dbValue *value)
Get double precision value.
Definition: value.c:50
#define GV_FACE
Definition: dig_defines.h:186
int ctype
Cache type.
Definition: dig_structs.h:507
Basic topology-related info.
Definition: dig_structs.h:784
plus_t Vect_get_num_primitives(const struct Map_info *Map, int type)
Get number of primitives in vector map.
Definition: level_two.c:47
int db_get_column_sqltype(dbColumn *column)
Returns column sqltype for column.
int Vect_is_3d(const struct Map_info *Map)
Check if vector map is 3D.
Data structure used for building pseudo-topology.
Definition: dig_structs.h:397
dbTable * db_get_cursor_table(dbCursor *cursor)
Get table allocated by cursor.
Definition: cursor.c:67
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
int db_get_table_number_of_columns(dbTable *table)
Return the number of columns of the table.
Non-native format info (PostGIS)
Definition: dig_structs.h:602
int Vect_cat_get(const struct line_cats *Cats, int field, int *cat)
Get first found category of given field.
char * geom_column
Geometry column (simple feature access)
Definition: dig_structs.h:631
struct Format_info_cache cache
Lines cache for reading feature.
Definition: dig_structs.h:683
Line topology.
Definition: dig_structs.h:1494
#define FALSE
Definition: gis.h:60
void * topo
Topology info.
Definition: dig_structs.h:1599
#define DB_NEXT
Definition: dbmi.h:114
Boundary topology.
Definition: dig_structs.h:1509
#define DB_C_TYPE_STRING
Definition: dbmi.h:107
struct Format_info_pg pg
PostGIS info.
Definition: dig_structs.h:726
int n_cats
Number of categories attached to element.
Definition: dig_structs.h:1715
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition: debug.c:65
dbValue * db_get_column_value(dbColumn *column)
Returns column value for given column structure.
#define GV_BOUNDARY
Definition: dig_defines.h:184
const struct driver * driver
Definition: driver/init.c:25
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1286
struct field_info * fi
Definition: dig_structs.h:653
off_t V1_rewrite_line_pg(struct Map_info *Map, off_t offset, int type, const struct line_pnts *points, const struct line_cats *cats)
Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only) ...
Definition: write_pg.c:179
Topological feature - node.
Definition: dig_structs.h:1448
int topo_geo_only
Topology format.
Definition: dig_structs.h:707
off_t V1_write_line_pg(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature on level 1 (PostGIS interface)
Definition: write_pg.c:99
#define DB_C_TYPE_INT
Definition: dbmi.h:108
int db_set_handle(dbHandle *handle, const char *dbName, const char *dbSchema)
Set handle (database and schema name)
Definition: handle.c:39
off_t V2_write_line_sfa(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature on level 2 (OGR/PostGIS interface, pseudo-topological level)
Definition: write_sfa.c:49
#define WITH_Z
Definition: dig_defines.h:171
struct dig_head head
Header info.
Definition: dig_structs.h:1403
int array_num
Number of items in offset list.
Definition: dig_structs.h:450
Definition: gis.h:498
plus_t Vect_get_num_nodes(const struct Map_info *Map)
Get number of nodes in vector map.
Definition: level_two.c:34
int srid
Spatial reference system id (see spatial_ref_sys table)
Definition: dig_structs.h:644
int * lines_types
List of line types (GV_POINT, GV_LINE, ...)
Definition: dig_structs.h:477
int array_alloc
Space allocated for offset list.
Definition: dig_structs.h:454
Vector map info.
Definition: dig_structs.h:1259
int V2_delete_line_sfa(struct Map_info *Map, off_t line)
Deletes feature on level 2 (OGR/PostGIS interface)
Definition: write_sfa.c:191
int V2__add_line_to_topo_nat(struct Map_info *Map, off_t offset, int type, const struct line_pnts *points, const struct line_cats *cats, int restore_line, int(*external_routine)(const struct Map_info *, int))
Add feature (line) to topology (internal use only)
Definition: write_nat.c:871
struct field_info * Vect_get_dblink(const struct Map_info *Map, int link)
Get information about link to database.
Definition: field.c:466
double * y
Array of Y coordinates.
Definition: dig_structs.h:1684
int Vect_get_line_nodes(const struct Map_info *Map, int line, int *n1, int *n2)
Get line nodes.
Definition: level_two.c:307
Area (topology) info.
Definition: dig_structs.h:1605
char * driver
Name of DB driver (&#39;sqlite&#39;, &#39;dbf&#39;, ...)
Definition: dig_structs.h:147
int dig_add_node(struct Plus_head *, double, double, double)
Add new node to plus structure.
Definition: plus_node.c:102
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Definition: dig_structs.h:689
void Vect_set_updated(struct Map_info *Map, int enable)
Enable/disable maintanance of list of updated lines/nodes.
Definition: level_two.c:458
#define DB_SEQUENTIAL
Definition: dbmi.h:123
int dig__byte_order_out()
Get byte order.
Definition: portable.c:1013
char * conninfo
Connection string.
Definition: dig_structs.h:607
#define DB_C_TYPE_DOUBLE
Definition: dbmi.h:109
#define LEVEL_2
Vector level - with 2D topology.
Definition: dig_defines.h:118
int V2__delete_line_from_topo_nat(struct Map_info *Map, int line, int type, const struct line_pnts *points, const struct line_cats *cats)
Delete feature from topology (internal use only)
Definition: write_nat.c:636
int number
Layer number.
Definition: dig_structs.h:139
void db_init_handle(dbHandle *handle)
Initialize handle (i.e database/schema)
Definition: handle.c:23
double S
South.
Definition: dig_structs.h:74
struct line_pnts ** lines
Lines array.
Definition: dig_structs.h:473
#define GV_RIGHT
Definition: dig_defines.h:175
int db_set_string(dbString *x, const char *s)
Inserts string to dbString (enlarge string)
Definition: string.c:41
int coor_dim
Coordinates dimension (2D or 3D)
Definition: dig_structs.h:639
#define GV_AREA
Definition: dig_defines.h:188
double * z
Array of Z coordinates.
Definition: dig_structs.h:1688
#define _(str)
Definition: glocale.h:13
Spatial index info.
Definition: dig_structs.h:1799
void Vect_reset_line(struct line_pnts *Points)
Reset line.
Definition: line.c:130
int db_get_value_int(dbValue *value)
Get integer value.
Definition: value.c:38
int * field
Array of layers (fields)
Definition: dig_structs.h:1707
char * G_str_replace(const char *buffer, const char *old_str, const char *new_str)
Replace all occurrences of old_str in buffer with new_str.
Definition: strings.c:188
#define GV_LINES
Definition: dig_defines.h:192
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
int db_open_database(dbDriver *driver, dbHandle *handle)
Open database connection.
Definition: c_opendb.c:27
void G_zero(void *buf, int i)
Zero out a buffer, buf, of length i.
Definition: gis/zero.c:23
FILE * G_fopen_old(const char *element, const char *name, const char *mapset)
Open a database file for reading.
Definition: gis/open.c:253
int V1_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, off_t offset)
Read feature from PostGIS layer at given offset (level 1 without topology)
Definition: read_pg.c:237
void db_free_string(dbString *x)
Free allocated space for dbString.
Definition: string.c:150
char * schema_name
Schema name.
Definition: dig_structs.h:615
dbColumn * db_get_table_column(dbTable *table, int idx)
Returns column structure for given table and column number.
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1136
char * getenv()
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1610
dbDriver * db_start_driver(const char *name)
Initialize a new dbDriver for db transaction.
Definition: start.c:50
int V2_delete_line_pg(struct Map_info *Map, off_t line)
Deletes feature on topological level (PostGIS interface)
Definition: write_pg.c:373
const char * db_get_value_string(dbValue *value)
Get string value.
Definition: value.c:92
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition: gis/error.c:204
const char * G_find_file2(const char *element, const char *name, const char *mapset)
Searches for a file from the mapset search list or in a specified mapset. (look but don&#39;t touch) ...
Definition: find_file.c:247
int db_open_select_cursor(dbDriver *driver, dbString *select, dbCursor *cursor, int mode)
Open select cursor.
Definition: c_openselect.c:37
void db_init_string(dbString *x)
Initialize dbString.
Definition: string.c:25
#define GV_KERNEL
Definition: dig_defines.h:187
char * fid_column
FID column.
Definition: dig_structs.h:627
#define DB_OK
Definition: dbmi.h:71
plus_t n_nodes
Current number of topological features derived from vector geometries.
Definition: dig_structs.h:939
SF_FeatureType
Simple feature types.
Definition: dig_defines.h:234
char * key
Name of key column (usually &#39;cat&#39;)
Definition: dig_structs.h:159