write_pg.c 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932
  1. /*!
  2. \file lib/vector/Vlib/write_pg.c
  3. \brief Vector library - write vector feature (PostGIS format)
  4. Higher level functions for reading/writing/manipulating vectors.
  5. Write subroutine inspired by OGR PostgreSQL driver.
  6. \todo PostGIS version of V2__add_line_to_topo_nat()
  7. \todo OGR version of V2__delete_area_cats_from_cidx_nat()
  8. \todo function to delete corresponding entry in fidx
  9. \todo OGR version of V2__add_area_cats_to_cidx_nat
  10. \todo OGR version of V2__add_line_to_topo_nat
  11. (C) 2012-2013 by Martin Landa, and the GRASS Development Team
  12. This program is free software under the GNU General Public License
  13. (>=v2). Read the file COPYING that comes with GRASS for details.
  14. \author Martin Landa <landa.martin gmail.com>
  15. */
  16. #include <string.h>
  17. #include <grass/vector.h>
  18. #include <grass/glocale.h>
  19. #include "local_proto.h"
  20. #ifdef HAVE_POSTGRES
  21. #include "pg_local_proto.h"
  22. #define WKBSRIDFLAG 0x20000000
  23. /*! Use SQL statements from PostGIS Topology extension (this options
  24. is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
  25. */
  26. #define USE_TOPO_STMT 0
  27. static off_t write_line_sf(struct Map_info *, int,
  28. const struct line_pnts **, int,
  29. const struct line_cats *);
  30. static off_t write_line_tp(struct Map_info *, int, int,
  31. const struct line_pnts *,
  32. const struct line_cats *);
  33. static char *binary_to_hex(int, const unsigned char *);
  34. static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
  35. static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
  36. int, int *);
  37. static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int,
  38. int, int *);
  39. static char *line_to_wkb(struct Format_info_pg *, const struct line_pnts **,
  40. int, int, int);
  41. static int write_feature(struct Map_info *, int, int,
  42. const struct line_pnts **, int, int, const struct field_info *);
  43. static char *build_insert_stmt(const struct Format_info_pg *, const char *,
  44. int, const struct field_info *);
  45. static int insert_topo_element(struct Map_info *, int, int, const char *);
  46. static int update_next_edge(struct Map_info*, int, int);
  47. static int delete_face(const struct Map_info *, int);
  48. static int update_topo_edge(struct Map_info *, int);
  49. static int update_topo_face(struct Map_info *, int);
  50. #endif
  51. static struct line_pnts *Points;
  52. /*!
  53. \brief Writes feature on level 1 (PostGIS interface)
  54. Notes for simple feature access:
  55. - centroids are not supported in PostGIS, pseudotopo holds virtual
  56. centroids
  57. - boundaries are not supported in PostGIS, pseudotopo treats polygons
  58. as boundaries
  59. Notes for PostGIS Topology access:
  60. - centroids are stored as isolated nodes
  61. - boundaries are stored as edges
  62. \param Map pointer to Map_info structure
  63. \param type feature type (GV_POINT, GV_LINE, ...)
  64. \param points pointer to line_pnts structure (feature geometry)
  65. \param cats pointer to line_cats structure (feature categories)
  66. \return feature offset into file
  67. \return -1 on error
  68. */
  69. off_t V1_write_line_pg(struct Map_info *Map, int type,
  70. const struct line_pnts *points,
  71. const struct line_cats *cats)
  72. {
  73. #ifdef HAVE_POSTGRES
  74. struct Format_info_pg *pg_info;
  75. pg_info = &(Map->fInfo.pg);
  76. if (pg_info->feature_type == SF_UNKNOWN) {
  77. /* create PostGIS table if doesn't exist */
  78. if (V2_open_new_pg(Map, type) < 0)
  79. return -1;
  80. }
  81. if (!pg_info->toposchema_name) { /* simple features access */
  82. return write_line_sf(Map, type, &points, 1, cats);
  83. }
  84. /* PostGIS Topology access */
  85. return write_line_tp(Map, type, FALSE, points, cats);
  86. #else
  87. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  88. return -1;
  89. #endif
  90. }
  91. /*!
  92. \brief Writes feature on topological level (PostGIS interface)
  93. Calls V2_write_line_sfa() for simple features access.
  94. \param Map pointer to Map_info structure
  95. \param type feature type (GV_POINT, GV_LINE, ...)
  96. \param points pointer to line_pnts structure (feature geometry)
  97. \param cats pointer to line_cats structure (feature categories)
  98. \return feature offset into file
  99. \return -1 on error
  100. */
  101. off_t V2_write_line_pg(struct Map_info *Map, int type,
  102. const struct line_pnts *points,
  103. const struct line_cats *cats)
  104. {
  105. #ifdef HAVE_POSTGRES
  106. struct Format_info_pg *pg_info;
  107. pg_info = &(Map->fInfo.pg);
  108. if (!pg_info->toposchema_name) { /* pseudo-topology */
  109. return V2_write_line_sfa(Map, type, points, cats);
  110. }
  111. /* PostGIS Topology */
  112. return write_line_tp(Map, type, FALSE, points, cats);
  113. #else
  114. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  115. return -1;
  116. #endif
  117. }
  118. /*!
  119. \brief Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only)
  120. Only for simple feature access. PostGIS Topology requires level 2.
  121. \todo Use UPDATE statement ?
  122. \param Map pointer to Map_info structure
  123. \param offset feature offset
  124. \param type feature type (GV_POINT, GV_LINE, ...)
  125. \param points feature geometry
  126. \param cats feature categories
  127. \return feature offset (rewriten feature)
  128. \return -1 on error
  129. */
  130. off_t V1_rewrite_line_pg(struct Map_info * Map,
  131. int line, int type, off_t offset,
  132. const struct line_pnts * points,
  133. const struct line_cats * cats)
  134. {
  135. G_debug(3, "V1_rewrite_line_pg(): line=%d type=%d offset=%lu",
  136. line, type, offset);
  137. #ifdef HAVE_POSTGRES
  138. if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
  139. G_warning(_("Unable to rewrite feature (incompatible feature types)"));
  140. return -1;
  141. }
  142. /* delete old */
  143. V1_delete_line_pg(Map, offset);
  144. return V1_write_line_pg(Map, type, points, cats);
  145. #else
  146. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  147. return -1;
  148. #endif
  149. }
  150. /*!
  151. \brief Rewrites feature at topological level (PostGIS interface, internal use only)
  152. Note: Topology must be built at level >= GV_BUILD_BASE
  153. \todo Handle also categories
  154. \todo Store original geometry in tmp table for restore
  155. \param Map pointer to Map_info structure
  156. \param type feature type (GV_POINT, GV_LINE, ...)
  157. \param line feature id
  158. \param points feature geometry
  159. \param cats feature categories
  160. \return offset where feature was rewritten
  161. \return -1 on error
  162. */
  163. off_t V2_rewrite_line_pg(struct Map_info *Map, int line, int type, off_t old_offset,
  164. const struct line_pnts *points, const struct line_cats *cats)
  165. {
  166. G_debug(3, "V2_rewrite_line_pg(): line=%d type=%d offset=%lu",
  167. line, type, old_offset);
  168. #ifdef HAVE_POSTGRES
  169. const char *schema_name, *table_name, *keycolumn;
  170. char *stmt, *geom_data;
  171. struct Format_info_pg *pg_info;
  172. geom_data = NULL;
  173. stmt = NULL;
  174. pg_info = &(Map->fInfo.pg);
  175. if (line < 1 || line > Map->plus.n_lines) {
  176. G_warning(_("Attempt to access feature with invalid id (%d)"), line);
  177. return -1;
  178. }
  179. if (type != V2_read_line_pg(Map, NULL, NULL, line)) {
  180. G_warning(_("Unable to rewrite feature (incompatible feature types)"));
  181. return -1;
  182. }
  183. if (pg_info->toposchema_name) { /* PostGIS Topology */
  184. schema_name = pg_info->toposchema_name;
  185. if (type & GV_POINTS) {
  186. table_name = keycolumn = "node";
  187. }
  188. else {
  189. table_name = "edge_data";
  190. keycolumn = "edge";
  191. }
  192. }
  193. else { /* simple features access */
  194. schema_name = pg_info->schema_name;
  195. table_name = pg_info->table_name;
  196. keycolumn = pg_info->fid_column;
  197. }
  198. geom_data = line_to_wkb(pg_info, &points, 1, type, Map->head.with_z);
  199. G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET geom = '%s'::GEOMETRY WHERE %s_id = %d",
  200. schema_name, table_name, geom_data, keycolumn, line);
  201. G_free(geom_data);
  202. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  203. G_warning(_("Unable to rewrite feature %d"), line);
  204. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  205. return -1;
  206. }
  207. return old_offset; /* offset not changed */
  208. #else
  209. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  210. return -1;
  211. #endif
  212. }
  213. /*!
  214. \brief Deletes feature at the given offset (level 1)
  215. Only for simple feature access. PostGIS Topology requires level 2.
  216. \param Map pointer Map_info structure
  217. \param offset feature offset
  218. \return 0 on success
  219. \return -1 on error
  220. */
  221. int V1_delete_line_pg(struct Map_info *Map, off_t offset)
  222. {
  223. #ifdef HAVE_POSTGRES
  224. long fid;
  225. char stmt[DB_SQL_MAX];
  226. struct Format_info_pg *pg_info;
  227. pg_info = &(Map->fInfo.pg);
  228. if (!pg_info->conn || !pg_info->table_name) {
  229. G_warning(_("No connection defined"));
  230. return -1;
  231. }
  232. if (offset >= pg_info->offset.array_num) {
  233. G_warning(_("Invalid offset (%d)"), offset);
  234. return -1;
  235. }
  236. fid = pg_info->offset.array[offset];
  237. G_debug(3, "V1_delete_line_pg(): offset = %lu -> fid = %ld",
  238. (unsigned long)offset, fid);
  239. if (!pg_info->inTransaction) {
  240. /* start transaction */
  241. pg_info->inTransaction = TRUE;
  242. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  243. return -1;
  244. }
  245. sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
  246. pg_info->table_name, pg_info->fid_column, fid);
  247. G_debug(2, "SQL: %s", stmt);
  248. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  249. G_warning(_("Unable to delete feature"));
  250. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  251. return -1;
  252. }
  253. return 0;
  254. #else
  255. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  256. return -1;
  257. #endif
  258. }
  259. /*!
  260. \brief Deletes feature on topological level (PostGIS interface)
  261. Note: Topology must be built at level >= GV_BUILD_BASE
  262. Calls V2_delete_line_sfa() for simple feature access.
  263. \param Map pointer to Map_info structure
  264. \param line feature id to be deleted
  265. \return 0 on success
  266. \return -1 on error
  267. */
  268. int V2_delete_line_pg(struct Map_info *Map, int line)
  269. {
  270. #ifdef HAVE_POSTGRES
  271. struct Format_info_pg *pg_info;
  272. pg_info = &(Map->fInfo.pg);
  273. if (line < 1 || line > Map->plus.n_lines) {
  274. G_warning(_("Attempt to access feature with invalid id (%d)"), line);
  275. return -1;
  276. }
  277. if (!pg_info->toposchema_name) { /* pseudo-topology */
  278. return V2_delete_line_sfa(Map, line);
  279. }
  280. else { /* PostGIS topology */
  281. int type, n_nodes;
  282. char stmt[DB_SQL_MAX];
  283. const char *table_name, *keycolumn;
  284. struct P_line *Line;
  285. if (line < 1 || line > Map->plus.n_lines) {
  286. G_warning(_("Attempt to access feature with invalid id (%d)"), line);
  287. return -1;
  288. }
  289. Line = Map->plus.Line[line];
  290. if (!Line) {
  291. G_warning(_("Attempt to access dead feature %d"), line);
  292. return -1;
  293. }
  294. if (Line->type & GV_POINTS) {
  295. table_name = keycolumn = "node";
  296. }
  297. else {
  298. table_name = "edge_data";
  299. keycolumn = "edge";
  300. /* first remove references to this edge */
  301. /* (1) left next edge */
  302. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_left_edge = edge_id, "
  303. "next_left_edge = -edge_id WHERE abs_next_left_edge = %ld",
  304. pg_info->toposchema_name, table_name, Line->offset);
  305. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  306. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  307. return -1;
  308. }
  309. /* (2) right next edge */
  310. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_right_edge = edge_id, "
  311. "next_right_edge = edge_id WHERE abs_next_right_edge = %ld",
  312. pg_info->toposchema_name, table_name, Line->offset);
  313. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  314. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  315. return -1;
  316. }
  317. }
  318. /* read the line */
  319. if (!Points) {
  320. Points = Vect_new_line_struct();
  321. }
  322. type = V2_read_line_pg(Map, Points, NULL, line);
  323. if (type < 0)
  324. return -1;
  325. /* delete record from topology table */
  326. sprintf(stmt, "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %ld",
  327. pg_info->toposchema_name, table_name, keycolumn, Line->offset);
  328. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  329. G_warning(_("Unable to delete feature (%s) %d"), keycolumn,
  330. line);
  331. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  332. return -1;
  333. }
  334. /* update topology */
  335. Vect_reset_updated(Map);
  336. if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
  337. return -1;
  338. /* delete nodes from 'nodes' table */
  339. n_nodes = Vect_get_num_updated_nodes(Map);
  340. if (n_nodes > 0) {
  341. int i, node;
  342. for (i = 0; i < n_nodes; i++) {
  343. node = Vect_get_updated_node(Map, i);
  344. if (node > 0)
  345. continue; /* node was updated, not deleted */
  346. node = abs(node);
  347. G_debug(3, "delete node %d from 'node' table", node);
  348. sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
  349. pg_info->toposchema_name, node);
  350. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  351. G_warning(_("Unable to delete node %d"), node);
  352. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  353. return -1;
  354. }
  355. }
  356. }
  357. return 0;
  358. }
  359. #else
  360. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  361. return -1;
  362. #endif
  363. }
  364. /*!
  365. \brief Writes node on topological level (PostGIS Topology interface, internal use only)
  366. \param Map pointer to Map_info structure
  367. \param points pointer to line_pnts structure
  368. \return 0 on success
  369. \return -1 on error
  370. */
  371. off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
  372. {
  373. #ifdef HAVE_POSTGRES
  374. struct Format_info_pg *pg_info;
  375. pg_info = &(Map->fInfo.pg);
  376. if (!pg_info->toposchema_name)
  377. return -1; /* PostGIS Topology required */
  378. return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
  379. #else
  380. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  381. return -1;
  382. #endif
  383. }
  384. /*!
  385. \brief Writes area on topological level (PostGIS Simple Features interface, internal use only)
  386. \param Map pointer to Map_info structure
  387. \param type feature type (GV_POINT, GV_LINE, ...)
  388. \param points pointer to line_pnts structure (boundary geometry)
  389. \param cats pointer to line_cats structure (feature categories)
  390. \param ipoints pointer to line_pnts structure (isles geometry)
  391. \param nisles number of isles
  392. \return feature offset into file
  393. \return -1 on error
  394. */
  395. off_t V2__write_area_pg(struct Map_info *Map,
  396. const struct line_pnts *bpoints,
  397. const struct line_cats *cats,
  398. const struct line_pnts **ipoints, int nisles)
  399. {
  400. #ifdef HAVE_POSTGRES
  401. int i;
  402. off_t ret;
  403. const struct line_pnts **points;
  404. if (nisles > 0) {
  405. points = (const struct line_pnts **) G_calloc(nisles + 1, sizeof(struct line_pnts *));
  406. points[0] = bpoints;
  407. for (i = 0; i < nisles; i++)
  408. points[i + 1] = ipoints[i];
  409. }
  410. else {
  411. points = &bpoints;
  412. }
  413. ret = write_line_sf(Map, GV_BOUNDARY, points, nisles + 1, cats);
  414. if (nisles > 0)
  415. G_free(points);
  416. return ret;
  417. #else
  418. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  419. return -1;
  420. #endif
  421. }
  422. #ifdef HAVE_POSTGRES
  423. /*!
  424. \brief Write vector features as PostGIS simple feature element
  425. \return 0 on success
  426. \return -1 on error
  427. */
  428. off_t write_line_sf(struct Map_info *Map, int type,
  429. const struct line_pnts **points, int nparts,
  430. const struct line_cats *cats)
  431. {
  432. int cat;
  433. off_t offset;
  434. SF_FeatureType sf_type;
  435. struct field_info *Fi;
  436. struct Format_info_pg *pg_info;
  437. struct Format_info_offset *offset_info;
  438. pg_info = &(Map->fInfo.pg);
  439. offset_info = &(pg_info->offset);
  440. /* check required PG settings */
  441. if (!pg_info->conn) {
  442. G_warning(_("No connection defined"));
  443. return -1;
  444. }
  445. if (!pg_info->table_name) {
  446. G_warning(_("PostGIS feature table not defined"));
  447. return -1;
  448. }
  449. /* create PostGIS table if doesn't exist */
  450. if (pg_info->feature_type == SF_UNKNOWN) {
  451. if (V2_open_new_pg(Map, type) < 0)
  452. return -1;
  453. }
  454. Fi = NULL; /* no attributes to be written */
  455. cat = -1;
  456. if (cats->n_cats > 0 && Vect_get_num_dblinks(Map) > 0) {
  457. /* check for attributes */
  458. Fi = Vect_get_dblink(Map, 0);
  459. if (Fi) {
  460. if (!Vect_cat_get(cats, Fi->number, &cat))
  461. G_warning(_("No category defined for layer %d"), Fi->number);
  462. if (cats->n_cats > 1) {
  463. G_warning(_("Feature has more categories, using "
  464. "category %d (from layer %d)"),
  465. cat, cats->field[0]);
  466. }
  467. }
  468. }
  469. sf_type = pg_info->feature_type;
  470. /* determine matching PostGIS feature geometry type */
  471. if (type & (GV_POINT | GV_KERNEL)) {
  472. if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
  473. G_warning(_("Feature is not a point. Skipping."));
  474. return -1;
  475. }
  476. }
  477. else if (type & GV_LINE) {
  478. if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
  479. G_warning(_("Feature is not a line. Skipping."));
  480. return -1;
  481. }
  482. }
  483. else if (type & GV_BOUNDARY || type & GV_CENTROID) {
  484. if (sf_type != SF_POLYGON) {
  485. G_warning(_("Feature is not a polygon. Skipping."));
  486. return -1;
  487. }
  488. }
  489. else if (type & GV_FACE) {
  490. if (sf_type != SF_POLYGON25D) {
  491. G_warning(_("Feature is not a face. Skipping."));
  492. return -1;
  493. }
  494. }
  495. else {
  496. G_warning(_("Unsupported feature type %d"), type);
  497. return -1;
  498. }
  499. G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d",
  500. type, points[0]->n_points, cat);
  501. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  502. /* skip this check when writing PostGIS topology */
  503. int part, npoints;
  504. for (part = 0; part < nparts; part++) {
  505. npoints = points[part]->n_points - 1;
  506. if (points[part]->x[0] != points[part]->x[npoints] ||
  507. points[part]->y[0] != points[part]->y[npoints] ||
  508. points[part]->z[0] != points[part]->z[npoints]) {
  509. G_warning(_("Boundary is not closed. Skipping."));
  510. return -1;
  511. }
  512. }
  513. }
  514. /* write feature's geometry and fid */
  515. if (-1 == write_feature(Map, -1, type, points, nparts, cat, Fi)) {
  516. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  517. return -1;
  518. }
  519. /* update offset array */
  520. if (offset_info->array_num >= offset_info->array_alloc) {
  521. offset_info->array_alloc += 1000;
  522. offset_info->array = (int *)G_realloc(offset_info->array,
  523. offset_info->array_alloc *
  524. sizeof(int));
  525. }
  526. offset = offset_info->array_num;
  527. offset_info->array[offset_info->array_num++] = cat;
  528. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  529. /* register first part in offset array */
  530. offset_info->array[offset_info->array_num++] = 0;
  531. }
  532. G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
  533. (unsigned long)offset, offset_info->array_num, cat);
  534. return offset;
  535. }
  536. /*!
  537. \brief Write vector feature in PostGIS topology schema and
  538. updates internal topology structures
  539. \param Map vector map
  540. \param type feature type to be written
  541. \param points feature geometry
  542. \param is_node TRUE for nodes (written as points)
  543. \return 0 on success
  544. \return -1 on error
  545. */
  546. off_t write_line_tp(struct Map_info *Map, int type, int is_node,
  547. const struct line_pnts *points,
  548. const struct line_cats *cats)
  549. {
  550. int line, cat;
  551. struct field_info *Fi;
  552. struct Format_info_pg *pg_info;
  553. struct Plus_head *plus;
  554. pg_info = &(Map->fInfo.pg);
  555. plus = &(Map->plus);
  556. /* check type for nodes */
  557. if (is_node && type != GV_POINT) {
  558. G_warning(_("Invalid feature type (%d) for nodes"), type);
  559. return -1;
  560. }
  561. /* check required PG settings */
  562. if (!pg_info->conn) {
  563. G_warning(_("No connection defined"));
  564. return -1;
  565. }
  566. if (!pg_info->table_name) {
  567. G_warning(_("PostGIS feature table not defined"));
  568. return -1;
  569. }
  570. if (!pg_info->toposchema_name) {
  571. G_warning(_("PostGIS topology schema not defined"));
  572. return -1;
  573. }
  574. /* create PostGIS table if doesn't exist */
  575. if (pg_info->feature_type == SF_UNKNOWN) {
  576. if (V2_open_new_pg(Map, type) < 0)
  577. return -1;
  578. }
  579. G_debug(3, "write_line_pg(): type = %d n_points = %d",
  580. type, points->n_points);
  581. line = -1; /* used only for topological access (lines, boundaries, and centroids) */
  582. Fi = NULL; /* no attributes to be written */
  583. cat = -1;
  584. if (cats && cats->n_cats > 0) {
  585. if (Vect_get_num_dblinks(Map) > 0) {
  586. /* check for attributes */
  587. Fi = Vect_get_dblink(Map, 0);
  588. if (Fi) {
  589. if (!Vect_cat_get(cats, Fi->number, &cat))
  590. G_warning(_("No category defined for layer %d"), Fi->number);
  591. if (cats->n_cats > 1) {
  592. G_warning(_("Feature has more categories, using "
  593. "category %d (from layer %d)"),
  594. cat, cats->field[0]);
  595. }
  596. }
  597. }
  598. /* assume layer=1 */
  599. Vect_cat_get(cats, 1, &cat);
  600. }
  601. /* update GRASS topology before writing PostGIS feature */
  602. if (is_node) {
  603. dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
  604. }
  605. else {
  606. int n_nodes;
  607. off_t offset;
  608. /* better is probably to check nextval directly */
  609. if (type & GV_POINTS) {
  610. offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
  611. offset += Vect_get_num_nodes(Map); /* nodes are also stored in 'node' table */
  612. }
  613. else { /* LINES */
  614. offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
  615. }
  616. Vect_reset_updated(Map);
  617. line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL, /* TODO: handle categories */
  618. -1, NULL);
  619. /* insert new nodes into 'nodes' table */
  620. n_nodes = Vect_get_num_updated_nodes(Map);
  621. if (n_nodes > 0) {
  622. int i, node;
  623. double x, y, z;
  624. if (!Points)
  625. Points = Vect_new_line_struct();
  626. for (i = 0; i < n_nodes; i++) {
  627. node = Vect_get_updated_node(Map, i);
  628. G_debug(3, " new node: %d", node);
  629. Vect_get_node_coor(Map, node, &x, &y, &z);
  630. Vect_reset_line(Points);
  631. Vect_append_point(Points, x, y, z);
  632. write_feature(Map, -1, GV_POINT, (const struct line_pnts **) &Points, 1,
  633. -1, NULL);
  634. }
  635. }
  636. }
  637. /* write new feature to PostGIS
  638. - feature table for simple features
  639. - feature table and topo schema for topological access
  640. */
  641. if (-1 == write_feature(Map, line, type, &points, 1, cat, Fi)) {
  642. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  643. return -1;
  644. }
  645. /* update PostGIS-line topo */
  646. if (plus->built >= GV_BUILD_BASE && (type & GV_LINES))
  647. update_topo_edge(Map, line);
  648. if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
  649. update_topo_face(Map, line);
  650. return 0;
  651. }
  652. /*!
  653. \brief Binary data to HEX
  654. Allocated buffer should be freed by G_free().
  655. \param nbytes number of bytes to allocate
  656. \param wkb_data WKB data
  657. \return allocated buffer with HEX data
  658. */
  659. char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
  660. {
  661. char *hex_data;
  662. int i, nlow, nhigh;
  663. static const char ach_hex[] = "0123456789ABCDEF";
  664. hex_data = (char *)G_malloc(nbytes * 2 + 1);
  665. hex_data[nbytes * 2] = '\0';
  666. for (i = 0; i < nbytes; i++) {
  667. nlow = wkb_data[i] & 0x0f;
  668. nhigh = (wkb_data[i] & 0xf0) >> 4;
  669. hex_data[i * 2] = ach_hex[nhigh];
  670. hex_data[i * 2 + 1] = ach_hex[nlow];
  671. }
  672. return hex_data;
  673. }
  674. /*!
  675. \brief Write point into WKB buffer
  676. See OGRPoint::exportToWkb from GDAL/OGR library
  677. \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
  678. \param points feature geometry
  679. \param with_z WITH_Z for 3D data
  680. \param[out] nsize buffer size
  681. \return allocated WKB buffer
  682. \return NULL on error
  683. */
  684. unsigned char *point_to_wkb(int byte_order,
  685. const struct line_pnts *points, int with_z,
  686. int *nsize)
  687. {
  688. unsigned char *wkb_data;
  689. unsigned int sf_type;
  690. if (points->n_points != 1)
  691. return NULL;
  692. /* allocate buffer */
  693. *nsize = with_z ? 29 : 21;
  694. wkb_data = G_malloc(*nsize);
  695. G_zero(wkb_data, *nsize);
  696. G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
  697. /* set the byte order */
  698. if (byte_order == ENDIAN_LITTLE)
  699. wkb_data[0] = '\001';
  700. else
  701. wkb_data[0] = '\000';
  702. /* set the geometry feature type */
  703. sf_type = with_z ? SF_POINT25D : SF_POINT;
  704. if (byte_order == ENDIAN_LITTLE)
  705. sf_type = LSBWORD32(sf_type);
  706. else
  707. sf_type = MSBWORD32(sf_type);
  708. memcpy(wkb_data + 1, &sf_type, 4);
  709. /* copy in the raw data */
  710. memcpy(wkb_data + 5, &(points->x[0]), 8);
  711. memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
  712. if (with_z) {
  713. memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
  714. }
  715. /* swap if needed */
  716. if (byte_order == ENDIAN_BIG) {
  717. SWAPDOUBLE(wkb_data + 5);
  718. SWAPDOUBLE(wkb_data + 5 + 8);
  719. if (with_z)
  720. SWAPDOUBLE(wkb_data + 5 + 16);
  721. }
  722. return wkb_data;
  723. }
  724. /*!
  725. \bried Write linestring into WKB buffer
  726. See OGRLineString::exportToWkb from GDAL/OGR library
  727. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  728. \param points feature geometry
  729. \param with_z WITH_Z for 3D data
  730. \param[out] nsize buffer size
  731. \return allocated WKB buffer
  732. \return NULL on error
  733. */
  734. unsigned char *linestring_to_wkb(int byte_order,
  735. const struct line_pnts *points, int with_z,
  736. int *nsize)
  737. {
  738. int i, point_size;
  739. unsigned char *wkb_data;
  740. unsigned int sf_type;
  741. if (points->n_points < 1)
  742. return NULL;
  743. /* allocate buffer */
  744. point_size = 8 * (with_z ? 3 : 2);
  745. *nsize = 5 + 4 + points->n_points * point_size;
  746. wkb_data = G_malloc(*nsize);
  747. G_zero(wkb_data, *nsize);
  748. G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
  749. /* set the byte order */
  750. if (byte_order == ENDIAN_LITTLE)
  751. wkb_data[0] = '\001';
  752. else
  753. wkb_data[0] = '\000';
  754. /* set the geometry feature type */
  755. sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
  756. if (byte_order == ENDIAN_LITTLE)
  757. sf_type = LSBWORD32(sf_type);
  758. else
  759. sf_type = MSBWORD32(sf_type);
  760. memcpy(wkb_data + 1, &sf_type, 4);
  761. /* copy in the data count */
  762. memcpy(wkb_data + 5, &(points->n_points), 4);
  763. /* copy in the raw data */
  764. for (i = 0; i < points->n_points; i++) {
  765. memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
  766. memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
  767. if (with_z) {
  768. memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
  769. }
  770. }
  771. /* swap if needed */
  772. if (byte_order == ENDIAN_BIG) {
  773. int npoints, nitems;
  774. npoints = SWAP32(points->n_points);
  775. memcpy(wkb_data + 5, &npoints, 4);
  776. nitems = (with_z ? 3 : 2) * points->n_points;
  777. for (i = 0; i < nitems; i++) {
  778. SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
  779. }
  780. }
  781. return wkb_data;
  782. }
  783. /*!
  784. \bried Write polygon into WKB buffer
  785. See OGRPolygon::exportToWkb from GDAL/OGR library
  786. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  787. \param ipoints list of ring geometries (first is outer ring)
  788. \param nrings number of rings
  789. \param with_z WITH_Z for 3D data
  790. \param[out] nsize buffer size
  791. \return allocated WKB buffer
  792. \return NULL on error
  793. */
  794. unsigned char *polygon_to_wkb(int byte_order,
  795. const struct line_pnts** points, int nrings,
  796. int with_z, int *nsize)
  797. {
  798. int i, ring, point_size, offset;
  799. unsigned char *wkb_data;
  800. unsigned int sf_type;
  801. /* check data validity */
  802. if (nrings < 1)
  803. return NULL;
  804. for (ring = 0; ring < nrings; ring++) {
  805. if (points[ring]->n_points < 3)
  806. return NULL;
  807. }
  808. /* allocate buffer */
  809. point_size = 8 * (with_z ? 3 : 2);
  810. *nsize = 9;
  811. for (ring = 0; ring < nrings; ring++)
  812. *nsize += 4 + point_size * points[ring]->n_points;
  813. wkb_data = G_malloc(*nsize);
  814. G_zero(wkb_data, *nsize);
  815. G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
  816. /* set the byte order */
  817. if (byte_order == ENDIAN_LITTLE)
  818. wkb_data[0] = '\001';
  819. else
  820. wkb_data[0] = '\000';
  821. /* set the geometry feature type */
  822. sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
  823. if (byte_order == ENDIAN_LITTLE)
  824. sf_type = LSBWORD32(sf_type);
  825. else
  826. sf_type = MSBWORD32(sf_type);
  827. memcpy(wkb_data + 1, &sf_type, 4);
  828. /* copy in the raw data */
  829. if (byte_order == ENDIAN_BIG) {
  830. int ncount;
  831. ncount = SWAP32(nrings);
  832. memcpy(wkb_data + 5, &ncount, 4);
  833. }
  834. else {
  835. memcpy(wkb_data + 5, &nrings, 4);
  836. }
  837. /* serialize rings */
  838. offset = 9;
  839. for (ring = 0; ring < nrings; ring++) {
  840. memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
  841. for (i = 0; i < points[ring]->n_points; i++) {
  842. memcpy(wkb_data + offset +
  843. 4 + point_size * i, &(points[ring]->x[i]), 8);
  844. memcpy(wkb_data + offset +
  845. 4 + 8 + point_size * i, &(points[ring]->y[i]), 8);
  846. if (with_z) {
  847. memcpy(wkb_data + offset +
  848. 4 + 16 + point_size * i, &(points[ring]->z[i]), 8);
  849. }
  850. }
  851. offset += 4 + point_size * points[ring]->n_points;
  852. /* swap if needed */
  853. if (byte_order == ENDIAN_BIG) {
  854. int npoints, nitems;
  855. npoints = SWAP32(points[ring]->n_points);
  856. memcpy(wkb_data + 5, &npoints, 4);
  857. nitems = (with_z ? 3 : 2) * points[ring]->n_points;
  858. for (i = 0; i < nitems; i++) {
  859. SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
  860. }
  861. }
  862. }
  863. return wkb_data;
  864. }
  865. /*!
  866. \brief Write feature to WKB buffer
  867. Allocated string buffer should be freed by G_free().
  868. \param pg_info pointer to Format_info_pg struct
  869. \param points array of geometries which form feature
  870. \param nparts number of geometries in array
  871. \param type feature type (GV_POINT, GV_LINE, ...)
  872. \param with_z WITH_Z for 3D data
  873. \return allocated string buffer
  874. \return NULL on error
  875. */
  876. char *line_to_wkb(struct Format_info_pg *pg_info,
  877. const struct line_pnts **points, int nparts, int type, int with_z)
  878. {
  879. int byte_order, nbytes, nsize;
  880. unsigned int sf_type;
  881. unsigned char *wkb_data;
  882. char *text_data, *text_data_p, *hex_data;
  883. byte_order = dig__byte_order_out();
  884. /* get wkb data */
  885. nbytes = -1;
  886. wkb_data = NULL;
  887. if (type & GV_POINTS) /* point or centroid */
  888. wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
  889. else if (type == GV_LINE)
  890. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  891. else if (type == GV_BOUNDARY) {
  892. if (!pg_info->toposchema_name) {
  893. /* PostGIS simple feature access */
  894. wkb_data = polygon_to_wkb(byte_order, points, nparts,
  895. with_z, &nbytes);
  896. }
  897. else {
  898. /* PostGIS topology access */
  899. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  900. }
  901. }
  902. if (!wkb_data || nbytes < 1) {
  903. G_warning(_("Unsupported feature type %d"), type);
  904. return NULL;
  905. }
  906. /* When converting to hex, each byte takes 2 hex characters. In
  907. addition we add in 8 characters to represent the SRID integer
  908. in hex, and one for a null terminator */
  909. nsize = nbytes * 2 + 8 + 1;
  910. text_data = text_data_p = (char *)G_malloc(nsize);
  911. /* convert the 1st byte, which is the endianess flag, to hex */
  912. hex_data = binary_to_hex(1, wkb_data);
  913. strcpy(text_data_p, hex_data);
  914. G_free(hex_data);
  915. text_data_p += 2;
  916. /* get the geom type which is bytes 2 through 5 */
  917. memcpy(&sf_type, wkb_data + 1, 4);
  918. /* add the SRID flag if an SRID is provided */
  919. if (pg_info->srid > 0) {
  920. unsigned int srs_flag;
  921. /* change the flag to little endianess */
  922. srs_flag = LSBWORD32(WKBSRIDFLAG);
  923. /* apply the flag */
  924. sf_type = sf_type | srs_flag;
  925. }
  926. /* write the geom type which is 4 bytes */
  927. hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
  928. strcpy(text_data_p, hex_data);
  929. G_free(hex_data);
  930. text_data_p += 8;
  931. /* include SRID if provided */
  932. if (pg_info->srid > 0) {
  933. unsigned int srs_id;
  934. /* force the srsid to little endianess */
  935. srs_id = LSBWORD32(pg_info->srid);
  936. hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
  937. strcpy(text_data_p, hex_data);
  938. G_free(hex_data);
  939. text_data_p += 8;
  940. }
  941. /* copy the rest of the data over - subtract 5 since we already
  942. copied 5 bytes above */
  943. hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
  944. strcpy(text_data_p, hex_data);
  945. G_free(hex_data);
  946. return text_data;
  947. }
  948. /*!
  949. \brief Insert feature into table
  950. \param Map pointer to Map_info structure
  951. \param line feature id (topo access only)
  952. \param type feature type (GV_POINT, GV_LINE, ...)
  953. \param points pointer to line_pnts struct
  954. \param nparts number of parts (rings for polygon)
  955. \param cat category number (-1 for no category)
  956. \param Fi pointer to field_info (attributes to copy, NULL for no attributes)
  957. \return -1 on error
  958. \retirn 0 on success
  959. */
  960. int write_feature(struct Map_info *Map, int line, int type,
  961. const struct line_pnts **points, int nparts,
  962. int cat, const struct field_info *Fi)
  963. {
  964. int with_z;
  965. char *stmt, *geom_data;
  966. struct Format_info_pg *pg_info;
  967. pg_info = &(Map->fInfo.pg);
  968. with_z = Map->head.with_z;
  969. if (with_z && pg_info->coor_dim != 3) {
  970. G_warning(_("Trying to insert 3D data into feature table "
  971. "which store 2D data only"));
  972. return -1;
  973. }
  974. if (!with_z && pg_info->coor_dim != 2) {
  975. G_warning(_("Trying to insert 2D data into feature table "
  976. "which store 3D data only"));
  977. return -1;
  978. }
  979. /* build WKB geometry from line_pnts structures */
  980. geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
  981. if (!geom_data)
  982. return -1;
  983. /* build INSERT statement
  984. simple feature geometry + attributes
  985. */
  986. stmt = build_insert_stmt(pg_info, geom_data, cat, Fi);
  987. G_debug(2, "SQL: %s", stmt);
  988. if (!pg_info->inTransaction) {
  989. /* start transaction */
  990. pg_info->inTransaction = TRUE;
  991. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
  992. G_free(geom_data);
  993. return -1;
  994. }
  995. }
  996. /* stmt can NULL when writing PostGIS topology with no attributes
  997. * attached */
  998. if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
  999. /* rollback transaction */
  1000. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1001. G_free(geom_data);
  1002. return -1;
  1003. }
  1004. G_free(stmt);
  1005. /* write feature in PostGIS topology schema if enabled */
  1006. if (pg_info->toposchema_name) {
  1007. /* insert feature into topology schema (node or edge) */
  1008. if (insert_topo_element(Map, line, type, geom_data) != 0) {
  1009. G_warning(_("Unable to insert topological element into PostGIS Topology schema"));
  1010. G_free(geom_data);
  1011. return -1;
  1012. }
  1013. }
  1014. G_free(geom_data);
  1015. return 0;
  1016. }
  1017. /*!
  1018. \brief Build INSERT statement to add new feature to the feature
  1019. table
  1020. Note: Allocated string should be freed.
  1021. \param pg_info pointer to Format_info_pg structure
  1022. \param geom_data geometry data
  1023. \param cat category number (or -1 for no category)
  1024. \param Fi pointer to field_info structure (NULL for no attributes)
  1025. \return allocated string with INSERT statement
  1026. */
  1027. char *build_insert_stmt(const struct Format_info_pg *pg_info,
  1028. const char *geom_data,
  1029. int cat, const struct field_info *Fi)
  1030. {
  1031. char *stmt, buf[DB_SQL_MAX];
  1032. stmt = NULL;
  1033. if (Fi && cat > -1) { /* write attributes (simple features and topology elements) */
  1034. int col, ncol, more;
  1035. int sqltype, ctype, is_fid;
  1036. char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
  1037. const char *colname;
  1038. dbString dbstmt;
  1039. dbCursor cursor;
  1040. dbTable *table;
  1041. dbColumn *column;
  1042. dbValue *value;
  1043. db_init_string(&dbstmt);
  1044. buf_val[0] = '\0';
  1045. /* read & set attributes */
  1046. sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
  1047. cat);
  1048. G_debug(4, "SQL: %s", buf);
  1049. db_set_string(&dbstmt, buf);
  1050. /* prepare INSERT statement */
  1051. sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
  1052. pg_info->schema_name, pg_info->table_name);
  1053. /* select data */
  1054. if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
  1055. &cursor, DB_SEQUENTIAL) != DB_OK) {
  1056. G_warning(_("Unable to select attributes for category %d"), cat);
  1057. }
  1058. else {
  1059. if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
  1060. G_warning(_("Unable to fetch data from table <%s>"),
  1061. Fi->table);
  1062. }
  1063. if (!more) {
  1064. G_warning(_("No database record for category %d, "
  1065. "no attributes will be written"), cat);
  1066. }
  1067. else {
  1068. table = db_get_cursor_table(&cursor);
  1069. ncol = db_get_table_number_of_columns(table);
  1070. for (col = 0; col < ncol; col++) {
  1071. column = db_get_table_column(table, col);
  1072. colname = db_get_column_name(column);
  1073. /* -> values */
  1074. value = db_get_column_value(column);
  1075. /* for debug only */
  1076. db_convert_column_value_to_string(column, &dbstmt);
  1077. G_debug(2, "col %d : val = %s", col,
  1078. db_get_string(&dbstmt));
  1079. sqltype = db_get_column_sqltype(column);
  1080. ctype = db_sqltype_to_Ctype(sqltype);
  1081. is_fid = strcmp(pg_info->fid_column, colname) == 0;
  1082. /* check fid column (must be integer) */
  1083. if (is_fid == TRUE &&
  1084. ctype != DB_C_TYPE_INT) {
  1085. G_warning(_("FID column must be integer, column <%s> ignored!"),
  1086. colname);
  1087. continue;
  1088. }
  1089. /* -> columns */
  1090. sprintf(buf_tmp, "%s", colname);
  1091. strcat(buf, buf_tmp);
  1092. if (col < ncol - 1)
  1093. strcat(buf, ",");
  1094. /* prevent writing NULL values */
  1095. if (!db_test_value_isnull(value)) {
  1096. switch (ctype) {
  1097. case DB_C_TYPE_INT:
  1098. sprintf(buf_tmp, "%d", db_get_value_int(value));
  1099. break;
  1100. case DB_C_TYPE_DOUBLE:
  1101. sprintf(buf_tmp, "%.14f",
  1102. db_get_value_double(value));
  1103. break;
  1104. case DB_C_TYPE_STRING: {
  1105. char *value_tmp;
  1106. value_tmp = G_str_replace(db_get_value_string(value), "'", "''");
  1107. sprintf(buf_tmp, "'%s'", value_tmp);
  1108. G_free(value_tmp);
  1109. break;
  1110. }
  1111. case DB_C_TYPE_DATETIME:
  1112. db_convert_column_value_to_string(column,
  1113. &dbstmt);
  1114. sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
  1115. break;
  1116. default:
  1117. G_warning(_("Unsupported column type %d"), ctype);
  1118. sprintf(buf_tmp, "NULL");
  1119. break;
  1120. }
  1121. }
  1122. else {
  1123. if (is_fid == TRUE)
  1124. G_warning(_("Invalid value for FID column: NULL"));
  1125. sprintf(buf_tmp, "NULL");
  1126. }
  1127. strcat(buf_val, buf_tmp);
  1128. if (col < ncol - 1)
  1129. strcat(buf_val, ",");
  1130. }
  1131. if (!pg_info->toposchema_name) {
  1132. /* simple feature access */
  1133. G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
  1134. buf, pg_info->geom_column, buf_val, geom_data);
  1135. }
  1136. else {
  1137. /* PostGIS topology access, write geometry in
  1138. * topology schema, skip geometry at this point */
  1139. if (buf[strlen(buf)-1] == ',') { /* last column skipped */
  1140. buf[strlen(buf)-1] = '\0';
  1141. buf_val[strlen(buf_val)-1] = '\0';
  1142. }
  1143. G_asprintf(&stmt, "%s) VALUES (%s)",
  1144. buf, buf_val);
  1145. }
  1146. }
  1147. }
  1148. }
  1149. else {
  1150. /* no attributes */
  1151. if (!pg_info->toposchema_name) {
  1152. /* no attributes (simple features access) */
  1153. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
  1154. "('%s'::GEOMETRY)",
  1155. pg_info->schema_name, pg_info->table_name,
  1156. pg_info->geom_column, geom_data);
  1157. }
  1158. else if (cat > 0) {
  1159. /* no attributes (topology elements) */
  1160. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES (NULL)",
  1161. pg_info->schema_name, pg_info->table_name,
  1162. pg_info->geom_column);
  1163. }
  1164. }
  1165. return stmt;
  1166. }
  1167. /*!
  1168. \brief Insert topological element into 'node' or 'edge' table
  1169. \param Map pointer to Map_info struct
  1170. \param line feature id (-1 for nodes/points)
  1171. \param type feature type (GV_POINT, GV_LINE, ...)
  1172. \param geom_data geometry in wkb
  1173. \return 0 on success
  1174. \return -1 on error
  1175. */
  1176. int insert_topo_element(struct Map_info *Map, int line, int type,
  1177. const char *geom_data)
  1178. {
  1179. char *stmt;
  1180. struct Format_info_pg *pg_info;
  1181. struct P_line *Line;
  1182. pg_info = &(Map->fInfo.pg);
  1183. if (line > 0)
  1184. Line = Map->plus.Line[line];
  1185. stmt = NULL;
  1186. switch(type) {
  1187. case GV_POINT: {
  1188. #if USE_TOPO_STMT
  1189. G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
  1190. pg_info->toposchema_name, geom_data);
  1191. #else
  1192. G_asprintf(&stmt, "INSERT INTO \"%s\".node (geom) VALUES ('%s'::GEOMETRY)",
  1193. pg_info->toposchema_name, geom_data);
  1194. #endif
  1195. break;
  1196. }
  1197. case GV_LINE:
  1198. case GV_BOUNDARY: {
  1199. #if USE_TOPO_STMT
  1200. G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
  1201. pg_info->toposchema_name, geom_data);
  1202. #else
  1203. int nle, nre;
  1204. if (!Line) {
  1205. G_warning(_("Topology not available. Unable to insert new edge."));
  1206. return -1;
  1207. }
  1208. struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
  1209. /* assuming isolated lines */
  1210. nle = -Line->offset;
  1211. nre = Line->offset;
  1212. G_debug(3, "new edge: id=%lu next_left_edge=%d next_right_edge=%d",
  1213. Line->offset, nle, nre);
  1214. G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (geom, start_node, end_node, "
  1215. "next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
  1216. "left_face, right_face) "
  1217. "VALUES ('%s'::GEOMETRY, %d, %d, %d, %d, %d, %d, 0, 0)",
  1218. pg_info->toposchema_name, geom_data, topo->N1, topo->N2, nle, abs(nle),
  1219. nre, abs(nre));
  1220. #endif
  1221. break;
  1222. }
  1223. case GV_CENTROID: {
  1224. #if USE_TOPO_STMT
  1225. G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
  1226. pg_info->toposchema_name, geom_data);
  1227. #else
  1228. if (!Line) {
  1229. G_warning(_("Topology not available. Unable to insert new node (centroid)"));
  1230. return -1;
  1231. }
  1232. struct P_topo_c *topo = (struct P_topo_c *) Line->topo;
  1233. /* get id - see write_line_tp()
  1234. sprintf(stmt_next, "SELECT nextval('\"%s\".node_node_id_seq')",
  1235. pg_info->toposchema_name);
  1236. Line->offset = Vect__execute_get_value_pg(pg_info->conn, stmt_next);
  1237. if (Line->offset < 1) {
  1238. G_warning(_("Invalid feature offset"));
  1239. return NULL;
  1240. }
  1241. */
  1242. G_asprintf(&stmt, "INSERT INTO \"%s\".node (containing_face, geom) "
  1243. "VALUES (%d, '%s'::GEOMETRY)",
  1244. pg_info->toposchema_name, topo->area, geom_data);
  1245. #endif
  1246. break;
  1247. }
  1248. default:
  1249. G_warning(_("Unsupported feature type %d"), type);
  1250. break;
  1251. }
  1252. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1253. /* rollback transaction */
  1254. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1255. return -1;
  1256. }
  1257. return 0;
  1258. }
  1259. /*!
  1260. \brief Find next line (topo only)
  1261. \param Map pointer to Map_info struct
  1262. \param nlines number of lines
  1263. \param line current line
  1264. \param[out] left left line
  1265. \param[out] right right line
  1266. \return left (line < 0) or right (line > 0) next edge
  1267. \return 0 on failure
  1268. */
  1269. int update_next_edge(struct Map_info* Map, int nlines, int line)
  1270. {
  1271. int ret, next_line, edge;
  1272. char stmt[DB_SQL_MAX];
  1273. const struct Format_info_pg *pg_info;
  1274. struct P_line *Line_next, *Line;
  1275. Line = Line_next = NULL;
  1276. pg_info = &(Map->fInfo.pg);
  1277. /* find next line
  1278. start node -> next on the left
  1279. end node -> next on the right
  1280. */
  1281. next_line = dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
  1282. G_debug(3, "line=%d next_line=%d", line, next_line);
  1283. if (next_line == 0) {
  1284. G_warning(_("Invalid topology"));
  1285. return 0;
  1286. }
  1287. Line = Map->plus.Line[abs(line)];
  1288. Line_next = Map->plus.Line[abs(next_line)];
  1289. if (!Line || !Line_next) {
  1290. G_warning(_("Invalid topology"));
  1291. return 0;
  1292. }
  1293. if (line > 0) {
  1294. edge = Line->offset;
  1295. ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
  1296. }
  1297. else {
  1298. edge = -Line->offset;
  1299. ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
  1300. }
  1301. if (next_line < 0) {
  1302. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
  1303. "abs_next_left_edge = %d WHERE edge_id = %lu AND abs_next_left_edge = %lu",
  1304. pg_info->toposchema_name, edge, abs(edge), Line_next->offset, Line_next->offset);
  1305. G_debug(3, "update edge=%lu next_left_edge=%d (?)", Line_next->offset, edge);
  1306. }
  1307. else {
  1308. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
  1309. "abs_next_right_edge = %d WHERE edge_id = %lu AND abs_next_right_edge = %lu",
  1310. pg_info->toposchema_name, edge, abs(edge), Line_next->offset, Line_next->offset);
  1311. G_debug(3, "update edge=%lu next_right_edge=%d (?)", Line_next->offset, edge);
  1312. }
  1313. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1314. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1315. return 0;
  1316. }
  1317. if (nlines > 2) {
  1318. /* more lines connected to the node
  1319. start node -> next on the right
  1320. end node -> next on the left
  1321. */
  1322. next_line = dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
  1323. Line_next = Map->plus.Line[abs(next_line)];
  1324. if (next_line < 0) {
  1325. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
  1326. "abs_next_left_edge = %d WHERE edge_id = %lu",
  1327. pg_info->toposchema_name, edge, abs(edge), Line_next->offset);
  1328. G_debug(3, "update edge=%lu next_left_edge=%d", Line_next->offset, edge);
  1329. }
  1330. else {
  1331. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
  1332. "abs_next_right_edge = %d WHERE edge_id = %lu",
  1333. pg_info->toposchema_name, edge, abs(edge), Line_next->offset);
  1334. G_debug(3, "update edge=%lu next_right_edge=%d", Line_next->offset, edge);
  1335. }
  1336. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1337. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1338. return 0;
  1339. }
  1340. }
  1341. return ret;
  1342. }
  1343. /*!
  1344. \brief Insert new face to the 'face' table (topo only)
  1345. \param Map pointer to Map_info struct
  1346. \param area area id (negative id for isles)
  1347. \return 0 on error
  1348. \return area id on success (>0)
  1349. */
  1350. int Vect__insert_face_pg(struct Map_info *Map, int area)
  1351. {
  1352. char *stmt;
  1353. struct Format_info_pg *pg_info;
  1354. struct bound_box box;
  1355. if (area == 0)
  1356. return 0; /* universal face has id '0' in PostGIS Topology */
  1357. stmt = NULL;
  1358. pg_info = &(Map->fInfo.pg);
  1359. /* check if face exists */
  1360. /* get mbr of the area */
  1361. if (area > 0)
  1362. Vect_get_area_box(Map, area, &box);
  1363. else
  1364. Vect_get_isle_box(Map, abs(area), &box);
  1365. /* insert face if not exists */
  1366. G_asprintf(&stmt, "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
  1367. "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f, "
  1368. "%.12f %.12f))', %d))", pg_info->toposchema_name, area,
  1369. box.W, box.S, box.W, box.N, box.E, box.N,
  1370. box.E, box.S, box.W, box.S, pg_info->srid);
  1371. G_debug(3, "new face id=%d", area);
  1372. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1373. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1374. return 0;
  1375. }
  1376. G_free(stmt);
  1377. return area;
  1378. }
  1379. /*!
  1380. \brief Delete existing face
  1381. \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
  1382. CONSTRAINTS ALL DEFERRED
  1383. \param Map pointer to Map_info struct
  1384. \param area area id to delete
  1385. \return 0 on success
  1386. \return -1 on error
  1387. */
  1388. int delete_face(const struct Map_info *Map, int area)
  1389. {
  1390. char stmt[DB_SQL_MAX];
  1391. const struct Format_info_pg *pg_info;
  1392. pg_info = &(Map->fInfo.pg);
  1393. /* update centroids first */
  1394. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 "
  1395. "WHERE containing_face = %d",
  1396. pg_info->toposchema_name, area);
  1397. G_debug(3, "SQL: %s", stmt);
  1398. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1399. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1400. return -1;
  1401. }
  1402. /* update also edges (left face) */
  1403. sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0 "
  1404. "WHERE left_face = %d",
  1405. pg_info->toposchema_name, area);
  1406. G_debug(3, "SQL: %s", stmt);
  1407. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1408. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1409. return -1;
  1410. }
  1411. /* update also edges (left face) */
  1412. sprintf(stmt, "UPDATE \"%s\".edge_data SET right_face = 0 "
  1413. "WHERE right_face = %d",
  1414. pg_info->toposchema_name, area);
  1415. G_debug(3, "SQL: %s", stmt);
  1416. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1417. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1418. return -1;
  1419. }
  1420. /* delete face */
  1421. sprintf(stmt, "DELETE FROM \"%s\".face WHERE face_id = %d",
  1422. pg_info->toposchema_name, area);
  1423. G_debug(3, "delete face id=%d", area);
  1424. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1425. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1426. return -1;
  1427. }
  1428. return 0;
  1429. }
  1430. /*!
  1431. \brief Update lines (next left and right edges)
  1432. - isolated edges
  1433. next left edge: -edge
  1434. next right edge: edge
  1435. - connected edges
  1436. next left edge: next edge or -edge
  1437. next right edge: next edge or edge
  1438. \param Map pointer to Map_info struct
  1439. \param line feature id
  1440. \return 0 on success
  1441. \return -1 on error
  1442. */
  1443. int update_topo_edge(struct Map_info *Map, int line)
  1444. {
  1445. int i, n;
  1446. int nle, nre, next_edge;
  1447. char stmt[DB_SQL_MAX];
  1448. struct Format_info_pg *pg_info;
  1449. struct P_line *Line;
  1450. pg_info = &(Map->fInfo.pg);
  1451. if (line < 1 || line > Map->plus.n_lines) {
  1452. G_warning(_("Attempt to access non-existing feature %d"), line);
  1453. return -1;
  1454. }
  1455. Line = Map->plus.Line[line];
  1456. if (!Line) {
  1457. G_warning(_("Attempt to access dead feature %d"), line);
  1458. return -1;
  1459. }
  1460. struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
  1461. nre = nle = 0; /* edge = 0 is an illegal value */
  1462. /* check for line connection */
  1463. for (i = 0; i < 2; i++) {
  1464. /* first check start node then end node */
  1465. n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
  1466. : Vect_get_node_n_lines(Map, topo->N2);
  1467. if (n < 2) /* no connection */
  1468. continue;
  1469. next_edge = update_next_edge(Map, n,
  1470. i == 0 ? line : -line);
  1471. if (next_edge != 0) {
  1472. if (i == 0)
  1473. nre = next_edge; /* update next right edge for start node */
  1474. else
  1475. nle = next_edge; /* update next left edge for end node */
  1476. }
  1477. else {
  1478. G_warning(_("Unable to determine next left/right edge"));
  1479. return -1;
  1480. }
  1481. }
  1482. if (nle == 0 && nre == 0) /* nothing changed */
  1483. return 0;
  1484. if (nle != 0 && nre != 0) {
  1485. /* update both next left and right edge */
  1486. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  1487. "next_left_edge = %d, abs_next_left_edge = %d, "
  1488. "next_right_edge = %d, abs_next_right_edge = %d "
  1489. "WHERE edge_id = %lu", pg_info->toposchema_name,
  1490. nle, abs(nle), nre, abs(nre), Line->offset);
  1491. }
  1492. else if (nle != 0) {
  1493. /* update next left edge only */
  1494. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  1495. "next_left_edge = %d, abs_next_left_edge = %d "
  1496. "WHERE edge_id = %lu", pg_info->toposchema_name,
  1497. nle, abs(nle), Line->offset);
  1498. }
  1499. else {
  1500. /* update next right edge only */
  1501. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  1502. "next_right_edge = %d, abs_next_right_edge = %d "
  1503. "WHERE edge_id = %lu", pg_info->toposchema_name,
  1504. nre, abs(nre), Line->offset);
  1505. }
  1506. G_debug(3, "update edge=%lu next_left_edge=%d next_right_edge=%d",
  1507. Line->offset, nle, nre);
  1508. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1509. /* rollback transaction */
  1510. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1511. return -1;
  1512. }
  1513. return 0;
  1514. }
  1515. /*!
  1516. \brief Update lines (left and right faces)
  1517. TODO: handle isles
  1518. \param Map pointer to Map_info struct
  1519. \param line feature id
  1520. \return 0 on success
  1521. \return -1 on error
  1522. */
  1523. int update_topo_face(struct Map_info *Map, int line)
  1524. {
  1525. int i, s, area, face[2];
  1526. char stmt[DB_SQL_MAX];
  1527. struct Format_info_pg *pg_info;
  1528. struct P_line *Line, *Line_i;
  1529. struct P_area *Area;
  1530. struct P_topo_b *topo, *topo_i;
  1531. pg_info = &(Map->fInfo.pg);
  1532. if (line < 1 || line > Map->plus.n_lines) {
  1533. G_warning(_("Attempt to access non-existing feature %d"), line);
  1534. return -1;
  1535. }
  1536. Line = Map->plus.Line[line];
  1537. if (!Line) {
  1538. G_warning(_("Attempt to access dead feature %d"), line);
  1539. return -1;
  1540. }
  1541. topo = (struct P_topo_b *)Line->topo;
  1542. /* for both side on the current boundary (line) */
  1543. /* create new faces */
  1544. for (s = 0; s < 2; s++) { /* for each side */
  1545. area = s == 0 ? topo->left : topo->right;
  1546. if (area <= 0) /* no area - skip */
  1547. continue;
  1548. face[s] = Vect__insert_face_pg(Map, area);
  1549. if (face[s] < 1) {
  1550. G_warning(_("Unable to create new face"));
  1551. return -1;
  1552. }
  1553. }
  1554. /* update edges forming faces */
  1555. for (s = 0; s < 2; s++) { /* for each side */
  1556. area = s == 0 ? topo->left : topo->right;
  1557. if (area <= 0) /* no area - skip */
  1558. continue;
  1559. Area = Map->plus.Area[area];
  1560. for (i = 0; i < Area->n_lines; i++) {
  1561. Line_i = Map->plus.Line[abs(Area->lines[i])];
  1562. topo_i = (struct P_topo_b *)Line_i->topo;
  1563. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  1564. "left_face = %d, right_face = %d "
  1565. "WHERE edge_id = %lu", pg_info->toposchema_name,
  1566. topo_i->left > 0 ? topo_i->left : 0,
  1567. topo_i->right > 0 ? topo_i->right : 0,
  1568. Line_i->offset);
  1569. G_debug(2, "SQL: %s", stmt);
  1570. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1571. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1572. return -1;
  1573. }
  1574. }
  1575. /* update also centroids (stored as nodes) */
  1576. if (Area->centroid > 0) {
  1577. Line_i = Map->plus.Line[Area->centroid];
  1578. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = %d "
  1579. "WHERE node_id = %lu", pg_info->toposchema_name,
  1580. face[s], Line_i->offset);
  1581. G_debug(2, "SQL: %s", stmt);
  1582. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1583. /* rollback transaction */
  1584. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1585. return -1;
  1586. }
  1587. }
  1588. }
  1589. return 0;
  1590. }
  1591. #endif