build_pg.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. /*!
  2. \file lib/vector/Vlib/build_pg.c
  3. \brief Vector library - Building topology for PostGIS layers
  4. Higher level functions for reading/writing/manipulating vectors.
  5. Line offset (simple features only) is
  6. - centroids : FID
  7. - other types : index of the first record (which is FID) in offset array.
  8. (C) 2012-2013 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. \author Martin Landa <landa.martin gmail.com>
  12. */
  13. #include <grass/vector.h>
  14. #include <grass/glocale.h>
  15. #ifdef HAVE_POSTGRES
  16. #include "pg_local_proto.h"
  17. static int build_topo(struct Map_info *, int);
  18. static int build_topogeom_stmt(const struct Format_info_pg *, int, int, int, char *);
  19. static int save_map_bbox(const struct Format_info_pg *, const struct bound_box*);
  20. static int create_topo_grass(const struct Format_info_pg *);
  21. static int has_topo_grass(const struct Format_info_pg *);
  22. static int write_nodes(const struct Plus_head *, const struct Format_info_pg *);
  23. static int write_areas(const struct Plus_head *, const struct Format_info_pg *);
  24. static int write_isles(const struct Plus_head *, const struct Format_info_pg *);
  25. static void build_stmt_id(const void *, int, int, const struct Plus_head *, char **, size_t *);
  26. #endif
  27. /*!
  28. \brief Build topology for PostGIS layer
  29. Build levels:
  30. - GV_BUILD_NONE
  31. - GV_BUILD_BASE
  32. - GV_BUILD_ATTACH_ISLES
  33. - GV_BUILD_CENTROIDS
  34. - GV_BUILD_ALL
  35. \param Map pointer to Map_info structure
  36. \param build build level
  37. \return 1 on success
  38. \return 0 on error
  39. */
  40. int Vect_build_pg(struct Map_info *Map, int build)
  41. {
  42. #ifdef HAVE_POSTGRES
  43. struct Plus_head *plus;
  44. struct Format_info_pg *pg_info;
  45. plus = &(Map->plus);
  46. pg_info = &(Map->fInfo.pg);
  47. G_debug(1, "Vect_build_pg(): db='%s' table='%s', build=%d",
  48. pg_info->db_name, pg_info->table_name, build);
  49. /* commit transaction block (update mode only) */
  50. if (pg_info->inTransaction && Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
  51. return 0;
  52. pg_info->inTransaction = FALSE;
  53. if (pg_info->feature_type == SF_UNKNOWN)
  54. return 1;
  55. if (build == plus->built)
  56. return 1; /* do nothing */
  57. /* TODO move this init to better place (Vect_open_ ?), because in
  58. theory build may be reused on level2 */
  59. if (!pg_info->toposchema_name && build >= plus->built && build > GV_BUILD_BASE) {
  60. G_free(pg_info->offset.array);
  61. G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
  62. }
  63. if (!pg_info->conn) {
  64. G_warning(_("No DB connection"));
  65. return 0;
  66. }
  67. if (!pg_info->fid_column && !pg_info->toposchema_name) {
  68. G_warning(_("Feature table <%s> has no primary key defined"),
  69. pg_info->table_name);
  70. G_warning(_("Random read is not supported for this layer. "
  71. "Unable to build topology."));
  72. return 0;
  73. }
  74. if (build > GV_BUILD_NONE) {
  75. G_message(_("Using external data format '%s' (feature type '%s')"),
  76. Vect_get_finfo_format_info(Map),
  77. Vect_get_finfo_geometry_type(Map));
  78. if (!pg_info->toposchema_name)
  79. G_message(_("Building pseudo-topology over simple features..."));
  80. else
  81. G_message(_("Building topology from PostGIS topology schema <%s>..."),
  82. pg_info->toposchema_name);
  83. }
  84. if (!pg_info->toposchema_name) /* pseudo-topology for simple features */
  85. return Vect__build_sfa(Map, build);
  86. /* PostGIS Topology */
  87. return build_topo(Map, build);
  88. #else
  89. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  90. return 0;
  91. #endif
  92. }
  93. #ifdef HAVE_POSTGRES
  94. /*!
  95. \brief Build from PostGIS topology schema
  96. \todo Attach isles
  97. \param Map pointer to Map_info struct
  98. \param build build level
  99. \return 1 on success
  100. \return 0 on error
  101. */
  102. int build_topo(struct Map_info *Map, int build)
  103. {
  104. int line, type, s;
  105. int area, nareas, isle, nisles;
  106. int face[2];
  107. char stmt[DB_SQL_MAX];
  108. struct Plus_head *plus;
  109. struct Format_info_pg *pg_info;
  110. struct P_line *Line;
  111. struct P_area *Area;
  112. struct P_topo_b *topo_b;
  113. struct P_isle *Isle;
  114. plus = &(Map->plus);
  115. pg_info = &(Map->fInfo.pg);
  116. /* check if upgrade or downgrade */
  117. if (build < plus->built) {
  118. /* -> downgrade */
  119. Vect__build_downgrade(Map, build);
  120. return 1;
  121. }
  122. /* -> upgrade */
  123. if (build < GV_BUILD_BASE)
  124. return 1; /* nothing to print */
  125. /* update TopoGeometry based on GRASS-like topology */
  126. Vect_build_nat(Map, build);
  127. /* store map boundig box in DB */
  128. save_map_bbox(pg_info, &(plus->box));
  129. /* begin transaction */
  130. if (Vect__execute_pg(pg_info->conn, "BEGIN"))
  131. return 0;
  132. /* write full node topo info to DB if requested */
  133. if (!pg_info->topo_geo_only) {
  134. write_nodes(plus, pg_info);
  135. }
  136. /* update faces from GRASS Topology */
  137. if (build >= GV_BUILD_AREAS) {
  138. /* do clean up (1-3)
  139. insert new faces (4)
  140. update edges (5)
  141. */
  142. /* 1) reset centroids to '0' (universal face) */
  143. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 WHERE "
  144. "containing_face IS NOT NULL", pg_info->toposchema_name);
  145. G_debug(2, "SQL: %s", stmt);
  146. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  147. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  148. return 0;
  149. }
  150. /* 2) reset left|right edges */
  151. sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0, right_face = 0",
  152. pg_info->toposchema_name);
  153. G_debug(2, "SQL: %s", stmt);
  154. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  155. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  156. return 0;
  157. }
  158. /* 3) delete faces (areas/isles) */
  159. sprintf(stmt, "DELETE FROM \"%s\".face WHERE "
  160. "face_id != 0", pg_info->toposchema_name);
  161. G_debug(2, "SQL: %s", stmt);
  162. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  163. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  164. return 0;
  165. }
  166. if (!pg_info->topo_geo_only) {
  167. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_AREA);
  168. G_debug(2, "SQL: %s", stmt);
  169. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  170. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  171. return 0;
  172. }
  173. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_ISLE);
  174. G_debug(2, "SQL: %s", stmt);
  175. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  176. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  177. return 0;
  178. }
  179. }
  180. /* 4) insert faces & update nodes (containing_face) based on
  181. * GRASS topology */
  182. G_message(_("Updating faces..."));
  183. nareas = Vect_get_num_areas(Map);
  184. for (area = 1; area <= nareas; area++) {
  185. G_percent(area, nareas, 5);
  186. if (0 == Vect__insert_face_pg(Map, area)) {
  187. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  188. return 0;
  189. }
  190. if (build < GV_BUILD_CENTROIDS)
  191. continue;
  192. /* update centroids (node -> containing_face) */
  193. Area = plus->Area[area];
  194. if (Area->centroid < 1) {
  195. G_debug(3, "Area %d without centroid, skipped", area);
  196. continue;
  197. }
  198. Line = plus->Line[Area->centroid];
  199. sprintf(stmt, "UPDATE \"%s\".node SET "
  200. "containing_face = %d WHERE node_id = %d",
  201. pg_info->toposchema_name, area, (int)Line->offset);
  202. G_debug(2, "SQL: %s", stmt);
  203. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  204. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  205. return 0;
  206. }
  207. }
  208. /* 5) update edges (left and right face) */
  209. G_message(_("Updating edges..."));
  210. for (line = 1; line <= plus->n_lines; line++) {
  211. G_percent(line, plus->n_lines, 5);
  212. type = Vect_read_line(Map, NULL, NULL, line);
  213. if (type != GV_BOUNDARY)
  214. continue;
  215. Line = Map->plus.Line[line];
  216. if (!Line) {
  217. G_warning(_("Inconsistency in topology detected. "
  218. "Dead line found."));
  219. return 0;
  220. }
  221. topo_b = (struct P_topo_b *) Line->topo;
  222. for (s = 0; s < 2; s++) { /* for both sides */
  223. face[s] = s == 0 ? topo_b->left : topo_b->right;
  224. if (face[s] < 0) {
  225. /* isle */
  226. Isle = plus->Isle[abs(face[s])];
  227. face[s] = Isle->area;
  228. }
  229. }
  230. G_debug(3, "update edge %d: left_face = %d, right_face = %d",
  231. (int)Line->offset, face[0], face[1]);
  232. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  233. "left_face = %d, right_face = %d "
  234. "WHERE edge_id = %d", pg_info->toposchema_name,
  235. face[0], face[1], (int) Line->offset);
  236. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  237. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  238. return 0;
  239. }
  240. }
  241. /* write full area topo info to DB if requested */
  242. if (!pg_info->topo_geo_only) {
  243. write_areas(plus, pg_info);
  244. }
  245. } /* build >= GV_BUILD_AREAS */
  246. if (build >= GV_BUILD_ATTACH_ISLES) {
  247. /* insert isles as faces with negative face_id */
  248. nisles = Vect_get_num_islands(Map);
  249. for(isle = 1; isle <= nisles; isle++) {
  250. Isle = plus->Isle[isle];
  251. Vect__insert_face_pg(Map, -isle);
  252. }
  253. /* write full isles topo info to DB if requested */
  254. if (!pg_info->topo_geo_only) {
  255. write_isles(plus, pg_info);
  256. }
  257. } /* build >= GV_BUILD_ISLES */
  258. if (pg_info->feature_type == SF_POLYGON) {
  259. int centroid;
  260. G_message(_("Updating TopoGeometry data..."));
  261. for (area = 1; area <= plus->n_areas; area++) {
  262. G_percent(area, plus->n_areas, 5);
  263. centroid = Vect_get_area_centroid(Map, area);
  264. if (centroid < 1)
  265. continue;
  266. /* update topogeometry object: centroid -> face */
  267. if (build_topogeom_stmt(pg_info, GV_CENTROID, area, centroid, stmt) &&
  268. Vect__execute_pg(pg_info->conn, stmt) == -1) {
  269. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  270. return 0;
  271. }
  272. }
  273. }
  274. if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
  275. return 0;
  276. return 1;
  277. }
  278. /*!
  279. \brief Build UPDATE statement for topo geometry element stored in
  280. feature table
  281. \param pg_info so pointer to Format_info_pg
  282. \param type feature type (GV_POINT, ...)
  283. \param topo_id topology element id
  284. \param fid feature id
  285. \param[out] stmt string buffer
  286. \return 1 on success
  287. \return 0 on failure
  288. */
  289. int build_topogeom_stmt(const struct Format_info_pg *pg_info,
  290. int type, int topo_id, int fid, char *stmt)
  291. {
  292. int topogeom_type;
  293. switch(type) {
  294. case GV_POINT:
  295. topogeom_type = 1;
  296. break;
  297. case GV_LINE:
  298. case GV_BOUNDARY:
  299. topogeom_type = 2;
  300. break;
  301. case GV_CENTROID:
  302. topogeom_type = 3;
  303. break;
  304. default:
  305. G_warning(_("Unsupported topo geometry type %d"), type);
  306. return 0;
  307. }
  308. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  309. "'(%d, 1, %d, %d)'::topology.TopoGeometry "
  310. "WHERE (%s).id = %d",
  311. pg_info->schema_name, pg_info->table_name,
  312. pg_info->topogeom_column, pg_info->toposchema_id,
  313. topo_id, topogeom_type, pg_info->topogeom_column, fid);
  314. return 1;
  315. }
  316. /*!
  317. \brief Store map bounding box in DB head table
  318. \param pg_info pointer to Format_info_pg struct
  319. \param box pointer to bounding box
  320. \return 1 on success
  321. \return 0 on failure
  322. */
  323. int save_map_bbox(const struct Format_info_pg *pg_info, const struct bound_box *box)
  324. {
  325. char stmt[DB_SQL_MAX];
  326. /* create if not exists */
  327. if (create_topo_grass(pg_info) == -1) {
  328. G_warning(_("Unable to create <%s.%s>"), TOPO_SCHEMA, TOPO_TABLE);
  329. return 0;
  330. }
  331. /* update bbox */
  332. if (has_topo_grass(pg_info)) {
  333. /* -> update */
  334. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  335. "'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d WHERE %s = %d",
  336. TOPO_SCHEMA, TOPO_TABLE, TOPO_BBOX,
  337. box->W, box->S, box->B, box->E, box->N, box->T,
  338. TOPO_ID, pg_info->toposchema_id);
  339. }
  340. else {
  341. /* -> insert */
  342. sprintf(stmt, "INSERT INTO \"%s\".\"%s\" (%s, %s) "
  343. "VALUES(%d, 'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d)",
  344. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX, pg_info->toposchema_id,
  345. box->W, box->S, box->B, box->E, box->N, box->T);
  346. }
  347. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  348. return -1;
  349. }
  350. return 1;
  351. }
  352. /*!
  353. \brief Creates 'topology.grass' table if not exists
  354. \return 0 table already exists
  355. \return 1 table successfully added
  356. \return -1 on error
  357. */
  358. int create_topo_grass(const struct Format_info_pg *pg_info)
  359. {
  360. char stmt[DB_SQL_MAX];
  361. PGresult *result;
  362. /* check if table exists */
  363. sprintf(stmt, "SELECT COUNT(*) FROM information_schema.tables "
  364. "WHERE table_schema = '%s' AND table_name = '%s'",
  365. TOPO_SCHEMA, TOPO_TABLE);
  366. result = PQexec(pg_info->conn, stmt);
  367. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  368. PQclear(result);
  369. return -1;
  370. }
  371. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  372. /* table already exists */
  373. PQclear(result);
  374. return 1;
  375. }
  376. PQclear(result);
  377. G_debug(1, "<%s.%s> created", TOPO_SCHEMA, TOPO_TABLE);
  378. /* create table */
  379. sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s INTEGER, %s box3d)",
  380. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX);
  381. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  382. return -1;
  383. }
  384. /* add primary key */
  385. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)",
  386. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID);
  387. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  388. return -1;
  389. }
  390. /* add constraint */
  391. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s_%s_fkey\" "
  392. "FOREIGN KEY (%s) REFERENCES topology.topology(id) ON DELETE CASCADE",
  393. TOPO_SCHEMA, TOPO_TABLE, TOPO_TABLE, TOPO_ID, TOPO_ID);
  394. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  395. return -1;
  396. }
  397. return 1;
  398. }
  399. /*!
  400. \brief Check if 'topology_id' exists in 'topology.grass'
  401. \param pg_info pointer to Format_info_pg struct
  402. \return TRUE if exists
  403. \return FALSE otherwise
  404. \return -1 on error
  405. */
  406. int has_topo_grass(const struct Format_info_pg *pg_info)
  407. {
  408. int has_topo;
  409. char stmt[DB_SQL_MAX];
  410. PGresult *result;
  411. sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".\"%s\" "
  412. "WHERE %s = %d",
  413. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
  414. result = PQexec(pg_info->conn, stmt);
  415. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  416. PQclear(result);
  417. return -1;
  418. }
  419. has_topo = FALSE;
  420. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  421. /* table already exists */
  422. has_topo = TRUE;
  423. }
  424. PQclear(result);
  425. return has_topo;
  426. }
  427. /*!
  428. \brief Insert node into 'node_grass' table
  429. Writes (see P_node struct):
  430. - lines
  431. - angles
  432. Already stored in Topo-Geo:
  433. - x,y,z (geom)
  434. \param plus pointer to Plus_head struct
  435. \param pg_info pointer to Format_info_pg struct
  436. \return 0 on success
  437. \return -1 on error
  438. */
  439. int write_nodes(const struct Plus_head *plus,
  440. const struct Format_info_pg *pg_info)
  441. {
  442. int i, node_id;
  443. size_t stmt_lines_size, stmt_angles_size;
  444. char *stmt_lines, *stmt_angles;
  445. char stmt[DB_SQL_MAX];
  446. const struct P_node *Node;
  447. const struct Format_info_offset *offset;
  448. offset = &(pg_info->offset);
  449. if (plus->n_nodes != offset->array_num)
  450. return -1;
  451. stmt_lines = stmt_angles = NULL;
  452. for (i = 1; i <= plus->n_nodes; i++) {
  453. Node = plus->Node[i];
  454. if (!Node)
  455. continue; /* should not happen */
  456. node_id = offset->array[i-1];
  457. /* 'lines' array */
  458. build_stmt_id(Node->lines, Node->n_lines, TRUE, plus, &stmt_lines, &stmt_lines_size);
  459. /* 'angle' array */
  460. build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles, &stmt_angles_size);
  461. /* build SQL statement to add new node into 'node_grass' */
  462. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  463. "%d, '{%s}', '{%s}')", pg_info->toposchema_name, TOPO_TABLE_NODE,
  464. node_id, stmt_lines, stmt_angles);
  465. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  466. return -1;
  467. }
  468. }
  469. G_free(stmt_lines);
  470. G_free(stmt_angles);
  471. return 0;
  472. }
  473. /*!
  474. \brief Insert area into 'area_grass' table
  475. Writes (see P_area struct):
  476. - lines
  477. - centroid
  478. - isles
  479. \param plus pointer to Plus_head struct
  480. \param pg_info pointer to Format_info_pg struct
  481. \return 0 on success
  482. \return -1 on error
  483. */
  484. int write_areas(const struct Plus_head *plus,
  485. const struct Format_info_pg *pg_info)
  486. {
  487. int area, centroid;
  488. size_t stmt_lines_size, stmt_isles_size;
  489. char *stmt_lines, *stmt_isles;
  490. char stmt[DB_SQL_MAX];
  491. const struct P_line *Line;
  492. const struct P_area *Area;
  493. stmt_lines = stmt_isles = NULL;
  494. for (area = 1; area <= plus->n_areas; area++) {
  495. Area = plus->Area[area];
  496. if (!Area)
  497. continue; /* should not happen */
  498. /* 'lines' array */
  499. build_stmt_id(Area->lines, Area->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  500. /* 'isles' array */
  501. build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles, &stmt_isles_size);
  502. Line = plus->Line[Area->centroid];
  503. if (!Line)
  504. return -1;
  505. centroid = (int) Line->offset;
  506. /* build SQL statement to add new node into 'node_grass' */
  507. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  508. "%d, '{%s}', %d, '{%s}')", pg_info->toposchema_name, TOPO_TABLE_AREA,
  509. area, stmt_lines, centroid, stmt_isles);
  510. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  511. return -1;
  512. }
  513. }
  514. G_free(stmt_lines);
  515. G_free(stmt_isles);
  516. return 0;
  517. }
  518. /*!
  519. \brief Insert isle into 'isle_grass' table
  520. Writes (see P_isle struct):
  521. - lines
  522. - area
  523. \param plus pointer to Plus_head struct
  524. \param pg_info pointer to Format_info_pg struct
  525. \return 0 on success
  526. \return -1 on error
  527. */
  528. int write_isles(const struct Plus_head *plus,
  529. const struct Format_info_pg *pg_info)
  530. {
  531. int isle;
  532. size_t stmt_lines_size;
  533. char *stmt_lines;
  534. char stmt[DB_SQL_MAX];
  535. const struct P_isle *Isle;
  536. stmt_lines = NULL;
  537. for (isle = 1; isle <= plus->n_isles; isle++) {
  538. Isle = plus->Isle[isle];
  539. if (!Isle)
  540. continue; /* should not happen */
  541. /* 'lines' array */
  542. build_stmt_id(Isle->lines, Isle->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  543. /* build SQL statement to add new node into 'node_grass' */
  544. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  545. "%d, '{%s}', %d)", pg_info->toposchema_name, TOPO_TABLE_ISLE,
  546. isle, stmt_lines, Isle->area);
  547. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  548. return -1;
  549. }
  550. }
  551. G_free(stmt_lines);
  552. return 0;
  553. }
  554. /*!
  555. \brief Create PG-like array for int/float array
  556. \param array array of items
  557. \param nitems number of items in the array
  558. \param is_int TRUE for array of integers otherwise floats
  559. \param plus pointer to Plus_head struct
  560. \param[in,out] output buffer (re-used)
  561. \param[in,out] buffer size
  562. */
  563. void build_stmt_id(const void *array, int nitems, int is_int, const struct Plus_head *plus,
  564. char **stmt, size_t *stmt_size)
  565. {
  566. int i, ivalue;
  567. int *iarray;
  568. float *farray;
  569. size_t stmt_id_size;
  570. char *stmt_id, buf_id[128];
  571. struct P_line *Line;
  572. if (is_int)
  573. iarray = (int *) array;
  574. else
  575. farray = (float *) array;
  576. if (!(*stmt)) {
  577. stmt_id_size = DB_SQL_MAX;
  578. stmt_id = (char *) G_malloc(stmt_id_size);
  579. }
  580. else {
  581. stmt_id_size = *stmt_size;
  582. stmt_id = *stmt;
  583. }
  584. /* reset array */
  585. stmt_id[0] = '\0';
  586. for (i = 0; i < nitems; i++) {
  587. /* realloc array if needed */
  588. if (strlen(stmt_id) + 100 > stmt_id_size) {
  589. stmt_id_size = strlen(stmt_id) + DB_SQL_MAX;
  590. stmt_id = (char *) G_realloc(stmt_id, stmt_id_size);
  591. }
  592. if (is_int) {
  593. if (plus) {
  594. Line = plus->Line[abs(iarray[i])];
  595. ivalue = (int) Line->offset;
  596. if (iarray[i] < 0)
  597. ivalue *= -1;
  598. }
  599. else {
  600. ivalue = iarray[i];
  601. }
  602. sprintf(buf_id, "%d", ivalue);
  603. }
  604. else {
  605. sprintf(buf_id, "%f", farray[i]);
  606. }
  607. if (i > 0)
  608. strcat(stmt_id, ",");
  609. strcat(stmt_id, buf_id);
  610. }
  611. *stmt = stmt_id;
  612. *stmt_size = stmt_id_size;
  613. }
  614. #endif